Ways to authenticate
There are several ways to set up authentication for Terraform when using the provider for Microsoft Fabric and on the top level can be divided into 2 main categories:
- User authentication
- Service Principal
User authentication is interactive and depends on using the Azure CLI and as such is not suitable for automating provisioning through CI/CD pipelines.
A Service Principal (SP) on the other hand is non-interactive in nature and is what you want to use for your automation.
Service Principal comes in a few flavors and my favorite and recommendation is to use a SP with OpenID Connect (OIDC). With this option you no longer have to worry about either expiring certificates or client secrets as it uses something called workload identity federation.
Limitations
To put it simply, the Terraform provider is a wrapper around the Microsoft Fabric REST API, meaning the authentication options are limited to whatever each API supports. I am regularly updating the table in this blog post on which Microsoft Fabric items which support service principal authentication. The table also includes which of those items again which has a Terraform resource published.
Creating a service principal as a federated identity credential
Since it is likely that there is a different team that manage your Azure estate than your Microsoft Fabric environment(s) I have made in this example the presumption that the Azure-team will implement the code below for you to have a dedicated service principal for your Microsoft Fabric automation tasks.
resource "azuread_application_registration" "iac_msfabric" {
display_name = "tf-app-iac-msfabric"
}
resource "azuread_application_federated_identity_credential" "iac_msfabric" {
application_id = azuread_application_registration.iac_msfabric.id
display_name = "tf-iac-msfabric-wif"
description = "Deployments for iac-msfabric repository"
audiences = ["api://AzureADTokenExchange"]
issuer = "https://token.actions.githubusercontent.com"
subject = "repo:MyGitHubAcctountOrOrganization/MyMsFabricTerraformCodeRepo:ref:refs/heads/main"
}
Configuring your Terraform environment for federated identity credential
In setting up your Terraform environment you want to make your configuration details as secure as possible. You can set it up in a way that the configuration details are stored as masked environment variables and that way if a bad actor (internal or external) gets read-access to your code repository the details are inaccessible.
Initializing Terraform
Before processing the Terraform code the working directory needs to be initialized using the terraform init
command. Specifically, it:
- Installs provider plugins: Terraform downloads and installs the providers referenced in your configuration (for example, Fabric, Azure, etc.) into a local directory (often called a plugin directory).
- Prepares the backend: If you use a remote or local backend for storing Terraform state, terraform init configures and initializes it.
- Validates required modules: Any referenced Terraform modules are located, downloaded, or otherwise prepared for use.
terraform init
is the very first command you should run in a new or cloned Terraform configuration folder, because it sets up everything Terraform needs for subsequent commands like terraform plan
or terraform apply
.
Executing the init-command like in this example you do not need reference the location of your remote state file in the terraform code block (see next paragraph).
terraform init \
-backend-config="resource_group_name=my-sa-rg" \
-backend-config="storage_account_name=mytfstatesa001" \
-backend-config="container_name=mytfstates" \
-backend-config="key=my-tf-env-state.tfstate"
In a future blog post I will go more in detail on how to configure the different Terraform task, including intitialization, for GitHub Action workflows and Azure DevOps pipelines.
Terraform configuration values
In your Terraform files folder you are required to have a terraform
code block and provider
code blocks. This blocks configures the Terraform behavior.
For readability always have the terraform block and provider block(s) in separate files:
tf / (working directory)
└── terraform.tf
└── provider.tf
In the code example below, when setting up the minimum (and recommended) required for service principal with workload identity federation this is what you need
terraform {
required_version = ">= 1.8, < 2.0"
backend "azurerm" {
use_oidc = true
}
required_providers {
# Microsoft Fabric
fabric = {
source = "microsoft/fabric"
}
}
}
#############################################
# Microsoft Fabric provider #
#############################################
provider "fabric" {
use_oidc = true
}
Environment variables
This configuration relies on environment varibles to be set. For local testing to make this work you need to set the following environment variables:
export FABRIC_TENANT_ID="00000000-0000-0000-0000-000000000000"
export FABRIC_CLIENT_ID="00000000-0000-0000-0000-000000000000"
The CLIENT_ID is the Application ID GUID for your application registration
Resources
Fabric Terraform provider - Authentication using a Service Principal and OpenID Connect (OIDC)
Terraform init command - Offical docs
Terraform block reference - Official docs