Create an Azure Service Principal for Terraform

To use Azure and Terraform together you need a way to authenticate against the Azure Tenant. Like you do as a user, applications need a way to do the same. But instead of creating a user account, we create a Service Principal account.

To do this we need to register an application in Azure Active Directory. Once we have the application registered then we can use it like a user account in Terraform to access Azure Subscriptions and create lots of cool resources.

Login to Azure and set the Subscription

The quickest way to do this is via the Azure CLI. You can do it via the web portal or PowerShell, but I find it much quicker through the CLI.

If you haven’t got Azure CLI installed on your machine. I suggest you look up how to install it on your system. If you are using Windows, and have chocolatey installed, the command is-

choco install azure-cli

1. log in to Azure as an Owner, using the Azure CLI.

az login

You will then be asked to open a web page and asked to log in. After you have logged in you can close the web page and go back to the CLI.

2, Check if you are in the correct subscription. If you have more than one subscription you’ll need to check you are in the right subscription. If you don’t have multiple subscriptions, skip this. If you don’t know if you have more than one then it would be wise to check.

az account list -output table

The subscription that has true in the IsDefault column is the subscription that is active, that you will build infrastructure in.

3. If you are not in the subscription that you want to be in, you can change it like so.

az account set --subscription "name of subscription"

After, running the command, run az account list --output table again to check the true in the IsDefault column has moved to the subscription you specified.

Whilst we are here, note down the Subscription ID for later, you will need it.

Create the Service Principal Account

To create the SP (Service Principal) run the command below.

az ad sp create-for-rbac --name nameofspaccount

This is the output.

{
  "appId": "40a6591a-e627-40e4-8424-55847a3aa45e",
  "displayName": "nameofspaccount",
  "name": "http://nameofspaccount",
  "password": "351df937-78da-345b-b245-fd45efd87e57",
  "tenant": "6....4"
}

I’ll quickly explain each line. The appId is the name of the application if you are asked to specify the Application ID or Client ID. That’s what they are after. The displayName and Name are not needed. The password is also known as the Application Secret or Client Secret. The last one is the Tenant ID. Yours will look different to what I have shown as I don’t want to publish my tenant ID. You will need the tenant ID as well.

The default role for the service principal is a Contributor. This has full permissions to read and write to the Azure account.

If you wanted to know the command to give the account access to the subscription. For this example, I will give the account contributor access to the whole subscription. Replace where it has SubscriptionID with your actual subscription ID, that you noted down earlier.

az ad sp create-for-rbac -n "nameofspaccount" --role contributor --scopes /subscriptions/subscriptionID

I’ll quickly cover some other commands whilst we are looking at them.

# This assigns the Reader role to the Application - replace the APP_ID with your Application ID
az role assignment create --assignee APP_ID --role Reader

# This deletes the Contributor role from the Application - replace the APP_ID with your Application ID
az role assignment delete --assignee APP_ID --role Contributor

# To check the assigned roles
az role assignment list -- assignee APP_ID

Verify the account

To verify the Service Principal account

# replace APP_ID, PASSWORD and TENANT_ID with the values outputted earlier.
az login --service-principal --username APP_ID --password PASSWORD --tenant TENANT_ID

Reset the password

If you need to reset the password.

# Replace APP_ID and NEW_PASSWORD
az ad sp reset-credentials --name APP_ID --password NEW_PASSWORD

Using the Service Principal in Terraform.

In Terraform, in the main.tf file, you add the following provider block.

provider "azurerm" {
  # whilst the `version` attribute is optional, we recommend pinning to a given version of the Provider
  version = "=2.20.0"
  features {}
}

In here you can add the information, Client_id, Client_secret, Subscription_ID etc… If you then initialized Terraform it would work. However, this is bad practice and should not be done as your sensitive information will be in the code.

  • features – (Required) A features block as defined below which can be used to customize the behaviour of certain Azure Provider resources.
  • client_id – (Optional) The Client ID which should be used. This can also be sourced from the ARM_CLIENT_ID Environment Variable.
  • environment – (Optional) The Cloud Environment which should be used. Possible values are publicusgovernmentgerman, and china. Defaults to public. This can also be sourced from the ARM_ENVIRONMENT environment variable.
  • subscription_id – (Optional) The Subscription ID which should be used. This can also be sourced from the ARM_SUBSCRIPTION_ID Environment Variable.
  • tenant_id – (Optional) The Tenant ID which should be used. This can also be sourced from the ARM_TENANT_ID Environment Variable.
  • client_secret – (Optional) The Client Secret which should be used. This can also be sourced from the ARM_CLIENT_SECRET Environment Variable.

Where should you put it?

You can put it in the terraform.tfvars file, and set .gitignore to ignore the tfvars file. but this is still not right. The best option is to make Environmental variables that you can reference in your code. To do this type the lines below in a Terminal.

For Windows machines, PowerShell.

# change the values to the values we gathered earlier
Set-Item -Path Env:ARM_CLIENT_ID -Value "APP_ID"
Set-Item -Path Env:ARM_CLIENT_SECRET -Value "APP_SECRET"
Set-Item -Path Env:ARM_SUBSCRIPTION_ID -Value "SUBSCRIPTION_ID"
Set-Item -Path Env:ARM_TENANT_ID -Value "TENANT_ID"

For Linux machines, Bash.

# change the values to the values we gathered earlier
export ARM_CLIENT_ID="APP_ID"
export ARM_CLIENT_SECRET="APP_SECRET"
export ARM_SUBSCRIPTION_ID="SUBSCRIPTION_ID"
export ARM_TENANT_ID="TENANT_ID"

Terraform will automatically pick up the Environment variables from your machine if it doesn’t reboot!

I hope this helped you understand how to add a Service Principal account and use it in Terraform.