Managing Azure Resources using Spacelift
Tags: devops, azure, cloud, cicd
Published: 2021-11-18 23:37:00

In this post I want to show how easy it is to create a [Spacelift](https://spacelift.io/) Stack that manages Azure resources. To keep things simple, I’m going to assume that you already know what Spacelift can do, and the basics of creating and running Stacks, but if not check out our [terraform-starter](https://github.com/spacelift-io/terraform-starter) repo that walks you through the process of setting up an account, and getting started.

#Overview
In order to allow Spacelift to manage Azure resources, we need to do the following:

* Create a Service Principal in Azure with the correct ARM permissions to manage our resources.
* Create a Stack in Spacelift that uses the [Azure](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs) provider.
* Give our Stack the credentials for our Service Principal.

All of the information you need to configure the Azure provider can be found [here](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure). This post just explains how to combine that information with Spacelift.

#Creating our Service Principal
To create our Service Principal, we need to use the `az ad sp create-for-rbac` command (replacing<subscription-id> with your actual subscription ID):

```hcl
az ad sp create-for-rbac --name spacelift-sp --role="Contributor" --scopes="/subscriptions/<subscription-id>"
```

This will output something like the following:

```hcl
Changing "spacelift-sp" to a valid URI of "http://spacelift-sp", which is the required format used for service principal names
Creating 'Contributor' role assignment under scope '/subscriptions/<subscription-id>'
The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
{
  "appId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  "displayName": "spacelift-sp",
  "name": "http://spacelift-sp",
  "password": "A4xFo3z-Hv9BMOFlmHfXP2ESXugHMdaike",
  "tenant": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
}
```

The command creates a new Service Principal called `spacelift-sp` , and grants it the *Contributor* role on your subscription. It also outputs the `appId`, `password` and `tenant` for the Service Principal. Make a note of these because you’ll need them later.

If you would rather assign permissions yourself later, you can run the following command to just create a Service Principal with no permissions:

```hcl
az ad sp create-for-rbac --name spacelift-sp --skip-assignment
```

You should now be able to view your Service Principal in the *Azure Active Directory -> App registrations* section of the Azure Portal:

![Azure Portal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufxxpkelf8uo70pxtlbr.png)

#Creating a Stack
Now that we’ve got a service principal, we need to create a Stack to actually create some Azure resources. To keep things simple all we’re going to do is add some terraform definitions that specify the version of the Azure Provider we want to use, and create an Azure Resource Group:

```hcl
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=2.46.0"
    }
  }
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "test-group" {
  name = "test-group"
  location = "West Europe"
}
```

If you commit that change and try to run your Stack just now, you should see something similar to the following failure:

![Azure error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8aywt0fw9iz2ti2t3b8.png)

Here’s the full error message:

*Error building AzureRM Client: obtain subscription() from Azure CLI: Error parsing json result from the Azure CLI: Error launching Azure CLI: exec: “az”: executable file not found in $PATH*

At first glance this error is slightly confusing, but what it means is that because we didn’t provide any credentials to the Azure provider it attempted to fallback to using the `az` CLI for authentication. This works fine locally, but won’t work on Spacelift.

To solve this, we need to provide our stack with our credentials, which is explained in the next section.

#Passing Credentials to the Stack

If you take a look at the [Terraform documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure), you’ll find that there are a number of different ways that you can authenticate Terraform with Azure. What we’re going to do here is authenticate using a Service Principal with a Client Secret, and provide that configuration via environment variables.

To do this we need to set the following environment variables, using the information returned by the Azure CLI when we created our Service Principal:

*ARM_CLIENT_ID — the `appId` from the response.
*ARM_CLIENT_SECRET — the `password` from the response.
*ARM_SUBSCRIPTION_ID — your subscription ID.
*ARM_TENANT_ID — the `tenant` from the response.

You can set these environment variables directly on your Stack, but what we’re going to do here is create a [Spacelift Context](https://docs.spacelift.io/concepts/configuration/context) and attach it to our Stack. This allows us to share the same credentials with multiple Stacks if we want.

Add a new Context via the Contexts screen in Spacelift:

![Spacelift Context](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0yceetfkjvc1rl66fnus.png)

Once your context has been created, edit it and enter your environment variables, making sure to mark the `ARM_CLIENT_SECRET` as a secret rather than plaintext:

![Spacelift Context](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0aixijgpxegviui78csm.png)

Now that we’ve got a Context containing our credentials, you can attach it to your Stack. To do this, click on the *Manage* link next to your Stack, go to *Contexts*, choose your Context, and click *Attach*:

![Azure-stack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5gzttpznkg2n2dmfjdf.png)

Now that your Context is attached, you should be able to see the it in the *Environment* section of your Stack:

![Environment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kz1frieqdbfqx5ugl86s.png)

If you try to run your Azure Stack again you should get a successful plan:

![Azure Stack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmfit8djns0donaiykyy.png)

And applying should now succeed:

![Azure Stack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aj2urczbbrvq11ueuzuv.png)

If you take a look in the Azure Portal, you should now be able to see the Resource Group that was created:

![Azure Portal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9q6hkanav9559u0d70ip.png)

#Credential Expiry
Before finishing up, I wanted to just point out a few things about the Service Principal credential expiry. Although this isn’t really related to Spacelift, it’s something that you need to be aware of.

When you create your Service Principal via the Azure CLI, it automatically creates a Client Secret for you with an expiry of 1 year:

![Client Secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hykl0csfmvsv6k3i650e.png)

What this means is that if you do absolutely nothing, your Spacelift Stacks will stop working after the credentials expire a year later. To solve this, all you need to do is generate a new Client Secret, and update the `ARM_CLIENT_SECRET` environment variable in your Context.

#Wrapping Up
Hopefully in this post I’ve shown that it’s simple to manage Azure resources using Spacelift. Once you’ve performed a little bit of initial setup, you should be able to rapidly get new Stacks up and running creating Azure resources.