microsoft / terraform-provider-azuredevops

Terraform Azure DevOps provider
https://www.terraform.io/docs/providers/azuredevops/
MIT License
387 stars 279 forks source link

Issue with creating AzureRM service connections using Workload Identity Federation (Automatic) with OpenID Connect authentication #1052

Open gurretl opened 6 months ago

gurretl commented 6 months ago

Community Note

Terraform (and Azure DevOps Provider) Version

Affected Resource(s)

Terraform Configuration Files

# The following environment variables are also set ARM_OIDC_TOKEN, ARM_CLIENT_ID, ARM_TENANT_ID.
provider "azuredevops" {
  use_oidc = true
  org_service_url       = var.azdo_org_service_url
}

resource "azuredevops_serviceendpoint_azurerm" "azure_connection" {
  project_id           = data.azuredevops_project.main.id
  azurerm_spn_tenantid = var.tenant_id
  description          = local.azdo_service_endpoint_description

  service_endpoint_name                  = local.azdo_service_endpoint_name
  azurerm_subscription_id                = var.subscription_id
  azurerm_subscription_name              = data.azurerm_subscription.main.display_name

  resource_group                         = var.resource_group_name

  service_endpoint_authentication_scheme = "WorkloadIdentityFederation"
  lifecycle {
    ignore_changes = [
      description,
    ]
  }
}

Debug Output

Panic Output

│ Error:  waiting for service endpoint ready. Error looking up service endpoint given ID (62a6e30c-2c7b-437e-adc6-bed673988263) and project ID (a40798b3-7beb-4418-9be0-5975fec1610a): map[severity:<nil> state:Failed statusMessage:A valid refresh token for identity 2c90afa0-8c58-7393-a184-7e31dcd1b866 was not found.]

The token seems to be valid for 10 minutes, and our pipeline duration is less than 3 minutes.

Expected Behavior

Actual Behavior

Steps to Reproduce

  1. Create manually a Workload Identity Federation (Automatic)
  2. Try to create a new Workload Identity Federation (Automatic) using the manually created one in an Azure Pipeline through Terraform with OpenID Authentication
  3. terraform apply

Important Factoids

With the PAT and the WIF “my-root-serviceconnection”, the underlying azure identity has the following permissions : • Azure DevOps : Project Collection Administrator. • Azure : "Application Developer" role, which allows it to create app registrations.

Permissions have been verified with Azure CLI.

References

https://registry.terraform.io/providers/microsoft/azuredevops/latest/docs/resources/serviceendpoint_azurerm#workload-identity-federation-automatic-azurerm-service-endpoint

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_oidc

xuzhang3 commented 6 months ago

As far as I know the token expiration time controlled by service, I cannot extend the token life time based on the request.

gurretl commented 6 months ago

Hi @xuzhang3

Thanks for your reply.

I found a solution, it was not related to the token I guess. I had to create more resources to be close to the "Automatic" method.

Maybe we just need to update the example provided in the Terraform documentation for the "Automatic" WIF service connection.

What I did is the following: I used it with an App registration resource (as it works behind the scenes in "Automatic") instead of a User assigned Managed Identity (manual).

resource "azuread_application" "application" {
  display_name = local.service_principal_name
}

resource "azuread_service_principal" "service_principal" {
  client_id = azuread_application.application.client_id
}

resource "azuredevops_serviceendpoint_azurerm" "azure_connection" {
  project_id                             = data.azuredevops_project.main.id
  service_endpoint_name                  = local.azdo_service_endpoint_name
  description                            = local.azdo_service_endpoint_description
  service_endpoint_authentication_scheme = "WorkloadIdentityFederation"
  credentials {
    serviceprincipalid = azuread_application.application.client_id
  }
  azurerm_spn_tenantid      = var.tenant_id
  azurerm_subscription_id   = var.subscription_id
  azurerm_subscription_name = data.azurerm_subscription.main.display_name
}

resource "azuread_application_federated_identity_credential" "main" {
  application_id = "/applications/${azuread_application.application.object_id}" 
  display_name   = "example-federated-credential"
  description    = "Test"
  audiences      = ["api://AzureADTokenExchange"]
  issuer         = azuredevops_serviceendpoint_azurerm.azure_connection.workload_identity_federation_issuer
  subject        = azuredevops_serviceendpoint_azurerm.azure_connection.workload_identity_federation_subject
}

If you use the example provided in the Terraform documentation, you may face the error I mentioned in this issue.