{"id":68720,"date":"2023-09-11T04:02:08","date_gmt":"2023-09-11T12:02:08","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/devops\/?p=68720"},"modified":"2024-03-07T03:31:19","modified_gmt":"2024-03-07T11:31:19","slug":"introduction-to-azure-devops-workload-identity-federation-oidc-with-terraform","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/introduction-to-azure-devops-workload-identity-federation-oidc-with-terraform\/","title":{"rendered":"Introduction to Azure DevOps Workload identity federation (OIDC) with Terraform"},"content":{"rendered":"<p>Welcome! This post is going to cover the end-to-end process of configuring and using <a href=\"https:\/\/devblogs.microsoft.com\/devops\/workload-identity-federation-for-azure-deployments-is-now-generally-available\/\">Workload identity federation<\/a> in Azure DevOps for your Terraform deployments. We&#8217;ll cover:<\/p>\n<ul>\n<li>Why use Workload identity federation<\/li>\n<li>What is Workload identity federation and how does it work<\/li>\n<li>How can your organisation start using Workload identity federation<\/li>\n<li>How to configure Workload identity federation using the Azure DevOps Terraform provider<\/li>\n<li>How to use Workload identity federation in an Azure DevOps Pipeline with the Terraform task<\/li>\n<\/ul>\n<h2>Why use\u00a0Workload identity federation<\/h2>\n<p>Up until now the only way to avoid storing service principal secrets for Azure DevOps pipelines was to use a self-hosted Azure DevOps agents with managed identities. Now with Workload identity federation we remove that limitation and enable you to use short-lived tokens for authenticating to Azure. This significantly improves your security posture and removes the need to figure out how to share and rotate secrets. Workload identity federation works with many Azure DevOps tasks, not just the Terraform ones we are focussing on in this article, so you can use it for deploying code and other configuration tasks. I encourage you to learn more about the supported tasks <a href=\"https:\/\/aka.ms\/azdo-rm-workload-identity-troubleshooting\">here<\/a>.<\/p>\n<h2>What is Workload identity federation and how does it work<\/h2>\n<p>Workload identity federation is an OpenID Connect implementation for Azure DevOps that allow you to use short-lived credential free authentication to Azure without the need to provision self-hosted agents with managed identity. You configure a trust between your Azure DevOps organisation and an Azure service principal. Azure DevOps then provides a token that can be used to authenticate to the Azure API.<\/p>\n<p>You can read more about how Workload identity federation works <a href=\"https:\/\/learn.microsoft.com\/en-us\/azure\/active-directory\/workload-identities\/workload-identity-federation\">here<\/a>.<\/p>\n<p>In the first iteration Workload identity federation for Azure DevOps works within the scope of a Service Connection. A new type of Azure Resource Manager Service Connection is available that enables this:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/ServiceConnectionTypes.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/ServiceConnectionTypes.png\" alt=\"Image ServiceConnectionTypes\" width=\"460\" height=\"448\" class=\"alignnone size-full wp-image-68725\" srcset=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/ServiceConnectionTypes.png 460w, https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/ServiceConnectionTypes-300x292.png 300w, https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/ServiceConnectionTypes-24x24.png 24w, https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/ServiceConnectionTypes-48x48.png 48w\" sizes=\"(max-width: 460px) 100vw, 460px\" \/><\/a><\/p>\n<p>Any task that supports a Service Connection and has been updated to use Workload identity federation can then use your configured trust to interact with Azure resources.<\/p>\n<p>On the Azure side we need to configure Federated Credentials on an App Registration or User Assigned Managed Identity service principal. There are a few settings needed for this, which you can find in the Azure DevOps Service Connection or you can figure out up front:<\/p>\n<ul>\n<li><code>Issuer URL<\/code>: This is the a URL in the format <code>https:\/\/vstoken.dev.azure.com\/&lt;organisation-id&gt;<\/code> where organisation-id is the GUID of your Azure DevOps organisation. For example <code>https:\/\/vstoken.dev.azure.com\/f66a4bc2-08ad-4ec0-a25e-e769dab3b294<\/code>.<\/li>\n<li><code>Subject identifier<\/code>: This is the mapping to your service connection in the format <code>sc:\/\/&lt;organisation-name&gt;\/&lt;project-name&gt;\/&lt;service-connection-name&gt;<\/code> where organisation-name is your Azure DevOps organisation name, project-name is your Azure DevOps project name and service-connection-name is the name of your service connection. For example <code>sc:\/\/my-organisation\/my-project\/my-service-connection<\/code>.<\/li>\n<li><code>Audience<\/code>: This is always <code>api:\/\/AzureADTokenExchange<\/code>.<\/li>\n<\/ul>\n<p>Here is what it looks like in the Azure Portal:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/MIFederatedCreds.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/MIFederatedCreds.png\" alt=\"Image MIFederatedCreds\" width=\"883\" height=\"517\" class=\"alignnone size-full wp-image-68727\" srcset=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/MIFederatedCreds.png 883w, https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/MIFederatedCreds-300x176.png 300w, https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/MIFederatedCreds-768x450.png 768w\" sizes=\"(max-width: 883px) 100vw, 883px\" \/><\/a><\/p>\n<p>Once we have the service connection and federated credentials configured, we can use the service connection to authenticate to Azure. You&#8217;ll just need to grant your service principal some permissions in the subscription.<\/p>\n<h2>How can your organisation start using Workload identity federation<\/h2>\n<p>Workload identity federation in now in public preview and turned on by default. If you want to check, you can find the\u00a0feature flag under the organisation &#8216;Preview Features&#8217; menu. If it is turned on you&#8217;ll see the new Azure Resource Manager Service Connection types. You can find lots more information\u00a0<a href=\"https:\/\/aka.ms\/azdo-rm-workload-identity\">here<\/a>.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/FeatureFlag.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/FeatureFlag.png\" alt=\"Image FeatureFlag\" width=\"695\" height=\"974\" class=\"alignnone size-full wp-image-68729\" srcset=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/FeatureFlag.png 695w, https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2024\/02\/FeatureFlag-214x300.png 214w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/p>\n<h2>How to configure Workload identity federation using the Azure DevOps Terraform provider<\/h2>\n<p>The <a href=\"https:\/\/registry.terraform.io\/providers\/microsoft\/azuredevops\/latest\" title=\"https:\/\/registry.terraform.io\/providers\/microsoft\/azuredevops\/latest\">Azure DevOps Terraform provider<\/a> has been updated to support the creation of Workload identity federation Service Connections. The documentation can be found <a href=\"https:\/\/registry.terraform.io\/providers\/microsoft\/azuredevops\/latest\/docs\/resources\/serviceendpoint_azurerm#workload-identity-federation-manual-azurerm-service-endpoint-subscription-scoped\" title=\"https:\/\/registry.terraform.io\/providers\/microsoft\/azuredevops\/latest\/docs\/resources\/serviceendpoint_azurerm#workload-identity-federation-manual-azurerm-service-endpoint-subscription-scoped\">here<\/a>.<\/p>\n<p>The following example shows how to setup a Service Connection and User Assigned Managed Identity with Federated Credentials:<\/p>\n<pre><code class=\"json\">terraform {\n  required_providers {\n    azurerm = {\n      source  = \"hashicorp\/azurerm\"\n      version = \"&gt;=3.0.0\"\n    }\n    azuredevops = {\n      source = \"microsoft\/azuredevops\"\n      version = \"&gt;= 0.9.0\"\n    }\n  }\n}\n\nprovider \"azurerm\" {\n  features {}\n}\n\nresource \"azuredevops_project\" \"example\" {\n  name               = \"Example Project\"\n  visibility         = \"private\"\n  version_control    = \"Git\"\n  work_item_template = \"Agile\"\n  description        = \"Managed by Terraform\"\n}\n\nresource \"azurerm_resource_group\" \"identity\" {\n  name     = \"identity\"\n  location = \"UK South\"\n}\n\nresource \"azurerm_user_assigned_identity\" \"example\" {\n  location            = azurerm_resource_group.identity.location\n  name                = \"example-identity\"\n  resource_group_name = azurerm_resource_group.identity.name\n}\n\nresource \"azuredevops_serviceendpoint_azurerm\" \"example\" {\n  project_id                             = azuredevops_project.example.id\n  service_endpoint_name                  = \"example-federated-sc\"\n  description                            = \"Managed by Terraform\"\n  service_endpoint_authentication_scheme = \"WorkloadIdentityFederation\"\n  credentials {\n    serviceprincipalid = azurerm_user_assigned_identity.example.client_id\n  }\n  azurerm_spn_tenantid      = \"00000000-0000-0000-0000-000000000000\"\n  azurerm_subscription_id   = \"00000000-0000-0000-0000-000000000000\"\n  azurerm_subscription_name = \"Example Subscription Name\"\n}\n\nresource \"azurerm_federated_identity_credential\" \"example\" {\n  name                = \"example-federated-credential\"\n  resource_group_name = azurerm_resource_group.identity.name\n  parent_id           = azurerm_user_assigned_identity.example.id\n  audience            = [\"api:\/\/AzureADTokenExchange\"]\n  issuer              = azuredevops_serviceendpoint_azurerm.example.workload_identity_federation_issuer\n  subject             = azuredevops_serviceendpoint_azurerm.example.workload_identity_federation_subject\n}\n<\/code><\/pre>\n<p>The items of note are:<\/p>\n<ul>\n<li>We have to supply the client id of our service principal in the <code>credentials<\/code> block service connection, so it knows which service principal is configured with federation.<\/li>\n<li>We supply the string <code>WorkloadIdentityFederation<\/code> in the <code>service_endpoint_authentication_scheme<\/code> attribute to tell it the type of service connection we want to create.<\/li>\n<li>We use the <code>workload_identity_federation_issuer<\/code> and <code>workload_identity_federation_subject<\/code> outputs of our service connection to populate the federated credentials. This is a shortcut for getting this information, which you of course get from elsewhere if you prefer.<\/li>\n<\/ul>\n<p>Once you run this Terraform and create these resources you&#8217;ll be ready to use the Service Connection in your Azure DevOps Pipeline.<\/p>\n<h2>How to use Workload identity federation in an Azure DevOps Pipeline with the Terraform task<\/h2>\n<p>We have updated the two most popular Terraform Tasks to support Workload identity federation, these are:<\/p>\n<ul>\n<li>Microsoft DevLabs Terraform:\u00a0<a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ms-devlabs.custom-terraform-tasks\">DevLabs Terraform<\/a><\/li>\n<li>Jason Johnson (formerly Charles Zipp) Azure Pipelines Terraform Tasks:\u00a0<a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=JasonBJohnson.azure-pipelines-tasks-terraform\">Azure Pipelines Terraform Tasks<\/a><\/li>\n<\/ul>\n<p>The following example shows how to use the Microsoft DevLabs task in an Azure DevOps Pipeline:<\/p>\n<pre><code class=\"yaml\">jobs:\n- deployment: deploy\n  displayName: Deploy with Terraform\n  pool: \n    vmImage: ubuntu-latest \n  environment: dev\n  strategy:\n    runOnce:\n      deploy:\n        steps:\n        - checkout: self\n          displayName: Checkout Terraform Module\n        - task: TerraformInstaller@0\n          displayName: Install Terraform\n          inputs:\n            terraformVersion: 'latest'\n        - task: TerraformTaskV4@4\n          displayName: Terraform Init\n          inputs:\n            provider: 'azurerm'\n            command: 'init'\n            workingDirectory: '$(workingDirectory)'\n            backendServiceArm: '${{ variables.serviceConnection }}'\n            backendAzureRmResourceGroupName: '$(BACKEND_AZURE_RESOURCE_GROUP_NAME)'\n            backendAzureRmStorageAccountName: '$(BACKEND_AZURE_STORAGE_ACCOUNT_NAME)'\n            backendAzureRmContainerName: '$(BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME)'\n            backendAzureRmKey: 'terraform.tfstate'\n          env:\n            ARM_USE_AZUREAD: true # This environment variable tells the backend to use AzureAD auth rather than trying a create a key. It means we can limit the permissions applied to the storage account and container to least priviledge: https:\/\/developer.hashicorp.com\/terraform\/language\/settings\/backends\/azurerm#use_azuread_auth\n        - task: TerraformTaskV4@4\n          displayName: Terraform Apply\n          inputs:\n            provider: 'azurerm'\n            command: 'apply'\n            workingDirectory: '$(workingDirectory)'\n            commandOptions: '-auto-approve -var=\"resource_group_name=$(AZURE_RESOURCE_GROUP_NAME)\"'\n            environmentServiceNameAzureRM: '${{ variables.serviceConnection }}'\n          env:\n            ARM_USE_AZUREAD: true\n<\/code><\/pre>\n<p>Right now you are probably thinking &#8220;how is this any different to what I do now?&#8221; and you&#8217;d be right to think that because there is no difference. The Workload identity federation implementation is abstracted away into the choice of Service Connection type. The task knows based on the type of Service Connection how to authenticate to Azure and set the relevant environment variables for you.<\/p>\n<p>If you want to know how you can do it yourself outside of the terraform task, you can see an example of using the Azure CLI task <a href=\"https:\/\/github.com\/Azure-Samples\/azure-devops-terraform-oidc-ci-cd\/blob\/8f8c0073a145ddbcbcda2d67d4e9027e317a5c37\/pipelines\/oidc.yml#L81\">here<\/a> and <a href=\"https:\/\/github.com\/Azure-Samples\/azure-devops-terraform-oidc-ci-cd\/blob\/8f8c0073a145ddbcbcda2d67d4e9027e317a5c37\/scripts\/set_terraform_azurerm_vars.ps1#L29\">here<\/a>.<\/p>\n<h2>Conclusion<\/h2>\n<p>We&#8217;ve shown you how to configure and use Workload identity federation for Azure DevOps, we want you to start using it right away.<\/p>\n<p>If you want a more in depth example, you can refer to <a href=\"https:\/\/github.com\/Azure-Samples\/azure-devops-terraform-oidc-ci-cd\">this sample repository<\/a>.<\/p>\n<p>Thanks for reading, feel free to ask questions.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You might have seen &#8220;Workload identity federation for Azure Deployments&#8221;\u00a0in the Azure DevOps Roadmap, well now it is in public preview and we&#8217;ve updated everything you need to start using it with Terraform today. Say goodbye to secrets when using Terraform for Azure with Azure DevOps.<\/p>\n","protected":false},"author":126620,"featured_media":68810,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-68720","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"acf":[],"blog_post_summary":"<p>You might have seen &#8220;Workload identity federation for Azure Deployments&#8221;\u00a0in the Azure DevOps Roadmap, well now it is in public preview and we&#8217;ve updated everything you need to start using it with Terraform today. Say goodbye to secrets when using Terraform for Azure with Azure DevOps.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/68720","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/126620"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=68720"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/68720\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/68810"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=68720"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=68720"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=68720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}