Authenticating using a Service Principal with a Client Certificate

Terraform supports a number of different methods for authenticating to Azure:


We recommend using either a Service Principal or Managed Identity when running Terraform non-interactively (such as when running Terraform in a CI server) - and authenticating using the Azure CLI when running Terraform locally.

Once you have configured a Service Principal as described in this guide, you should follow the Configuring a Service Principal for managing Azure Active Directory guide to grant the Service Principal necessary permissions to create and modify Azure Active Directory objects such as users and groups.


Setting up an Application and Service Principal

A Service Principal is a security principal within Azure Active Directory which can be granted permissions to manage objects in Azure Active Directory. To authenticate with a Service Principal, you will need to create an Application object within Azure Active Directory, which you will use as a means of authentication, either using a Client Secret, OpenID Connect, or Client Certificate (which is documented in this guide). This can be done using the Azure Portal.

This guide will cover how to generate a client certificate, how to create an Application and linked Service Principal, and then how to assign the Client Certificate to the Application so that it can be used for authentication.


Generating a Client Certificate

Firstly we need to create a certificate which can be used for authentication. To do that we're going to generate a private key and self-signed certificate using OpenSSL or LibreSSL (this can also be achieved using PowerShell, however that's outside the scope of this document):

$ openssl req -subj '/CN=myclientcertificate/O=HashiCorp, Inc./ST=CA/C=US' \
    -new -newkey rsa:4096 -sha256 -days 730 -nodes -x509 -keyout client.key -out client.crt

Next we generate a PKCS#12 bundle (.pfx file) which can be used by the AzureAD provider to authenticate with Azure:

$ openssl pkcs12 -export -password pass:"Pa55w0rd123" -out client.pfx -inkey client.key -in client.crt

Now that we've generated a certificate, we can create the Azure Active Directory Application.


Creating the Application and Service Principal

We're going to create the Application in the Azure Portal - to do this navigate to the Azure Active Directory overview within the Azure Portal - then select the App Registrations blade. Click the New registration button at the top to add a new Application within Azure Active Directory. On this page, set the following values then press Create:

At this point the newly created Azure Active Directory application should be visible on-screen - if it's not, navigate to the App Registration blade and select the newly created Azure Active Directory application.

At the top of this page, you'll need to take note of the "Application (client) ID" and the "Directory (tenant) ID", which you can use for the values of client_id and tenant_id respectively.

Assigning the Client Certificate to the Azure Active Directory Application

To associate the public portion of the Client Certificate with the Azure Active Directory Application, select Certificates & secrets. This screen displays the Certificates and Client Secrets (i.e. passwords) which are associated with this Azure Active Directory Application.

The Public Key associated with the generated Certificate can be uploaded by selecting Upload Certificate, selecting the file which should be uploaded (in the example above, this would be client.crt) - and then hitting Add.


Configuring Terraform to use the Client Certificate

Now that we have our Client Certificate uploaded to Azure and ready to use, it's possible to configure Terraform in a few different ways.

The provider can be configured to read the certificate bundle from the .pfx file in your filesystem, or alternatively you can pass a base64-encoded copy of the certificate bundle directly to the provider.

Environment Variables

Our recommended approach is storing the credentials as Environment Variables, for example:

Reading the certificate bundle from the filesystem

# sh
$ export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000"
$ export ARM_CLIENT_CERTIFICATE_PATH="/path/to/my/client/certificate.pfx"
$ export ARM_CLIENT_CERTIFICATE_PASSWORD="Pa55w0rd123"
$ export ARM_TENANT_ID="10000000-2000-3000-4000-500000000000"
# PowerShell
> $env:ARM_CLIENT_ID = "00000000-0000-0000-0000-000000000000"
> $env:ARM_CLIENT_CERTIFICATE_PATH = "/path/to/my/client/certificate.pfx"
> $env:ARM_CLIENT_CERTIFICATE_PASSWORD = "Pa55w0rd123"
> $env:ARM_TENANT_ID = "10000000-2000-3000-4000-500000000000"

Passing the encoded certificate bundle directly

# sh
$ export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000"
$ export ARM_CLIENT_CERTIFICATE="$(base64 /path/to/my/client/certificate.pfx)"
$ export ARM_CLIENT_CERTIFICATE_PASSWORD="Pa55w0rd123"
$ export ARM_TENANT_ID="10000000-2000-3000-4000-500000000000"
# PowerShell
> $env:ARM_CLIENT_ID = "00000000-0000-0000-0000-000000000000"
> $env:ARM_CLIENT_CERTIFICATE = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes("/path/to/my/client/certificate.pfx"))
> $env:ARM_CLIENT_CERTIFICATE_PASSWORD = "Pa55w0rd123"
> $env:ARM_TENANT_ID = "10000000-2000-3000-4000-500000000000"

At this point running either terraform plan or terraform apply should allow Terraform to authenticate using the Client Certificate.

Next you should follow the Configuring a Service Principal for managing Azure Active Directory guide to grant the Service Principal necessary permissions to create and modify Azure Active Directory objects such as users and groups.

Provider Block

It's also possible to configure these variables either directly, or from variables, in your provider block, like so:

Reading the certificate bundle from the filesystem

variable "client_certificate_path" {}
variable "client_certificate_password" {}

provider "azuread" {
  client_id                   = "00000000-0000-0000-0000-000000000000"
  client_certificate_path     = var.client_certificate_path
  client_certificate_password = var.client_certificate_password
  tenant_id                   = "10000000-2000-3000-4000-500000000000"
}

Passing the encoded certificate bundle directly

variable "client_certificate" {}
variable "client_certificate_password" {}

provider "azuread" {
  client_id                   = "00000000-0000-0000-0000-000000000000"
  client_certificate          = var.client_certificate
  client_certificate_password = var.client_certificate_password
  tenant_id                   = "10000000-2000-3000-4000-500000000000"
}

More information on the fields supported in the Provider block can be found here.

At this point running either terraform plan or terraform apply should allow Terraform to authenticate using the Client Certificate.

Next you should follow the Configuring a Service Principal for managing Azure Active Directory guide to grant the Service Principal necessary permissions to create and modify Azure Active Directory objects such as users and groups.