pulumi-azure-native icon indicating copy to clipboard operation
pulumi-azure-native copied to clipboard

Add support for Azure Pipelines credential

Open AndreasMWalter opened this issue 3 months ago • 9 comments

What happened?

pulumi 3.178.0 azure-native 3.4.0

We are running pulumi on Azure Pipelines using the Service Connections IdToken as ARM_OIDC_TOKEN

On long running operations we encounter the following error:

AADSTS700024: Client assertion is not within its valid time range. Current time: 2025-08-14T14:05:49.7389289Z, assertion valid from 2025-08-14T13:30:53.0000000Z, expiry time of assertion 2025-08-14T13:40:53.0000000Z.

We have had this issue occur recently on our terraform pipelines as well, seems Microsoft has changed the Token Lifetime recently, however we could fix it by setting the following ENV vars:

ARM_OIDC_AZURE_SERVICE_CONNECTION_ID
ARM_OIDC_REQUEST_URL
ARM_OIDC_REQUEST_TOKEN

As per Azure Devops Terraform Task and Improve Security Posture in Service Connections our task looks like this:

- task: AzureCLI@2
  env:
    ARM_OIDC_REQUEST_TOKEN: $(System.AccessToken)
    AZDO_ORG_SERVICE_URL: $(System.CollectionUri)
    SYSTEM_TEAMPROJECTID: $(System.TeamProjectId)
  inputs:
    azureSubscription: SERVICECONNECTIONNAME
    scriptType: "pscore"
    scriptLocation: "inlineScript"
    inlineScript: |
      $env:ARM_USE_OIDC = "true"
      $env:ARM_OIDC_AZURE_SERVICE_CONNECTION_ID = $env:AZURESUBSCRIPTION_SERVICE_CONNECTION_ID
      $env:ARM_OIDC_REQUEST_URL=$env:SYSTEM_OIDCREQUESTURI 
      $env:ARM_CLIENT_ID =  $env:AZURESUBSCRIPTION_CLIENT_ID
      $env:ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv)
      $env:ARM_TENANT_ID = $env:AZURESUBSCRIPTION_TENANT_ID       
      terraform apply
  displayName: Terraform Apply

Replicating this to pulumi azure-native is not possible, as the required ENV Vars are not supported. Creating a workaround by using ${SYSTEM_OIDCREQUESTURI}?api-version=7.1&serviceConnectionId=${AZURESUBSCRIPTION_SERVICE_CONNECTION_ID} and feeding it into ARM_OIDC_REQUEST_URL does not work as this auth_azidentity uses the Get Method which is probably Github related and Azure Pipelines expects it to be Post Method as found here

The relevant go documentation on the AzurePipelinesCredential can be found here

Example

So I don't have a particular example on this, it mainly happens on long running pipeline operations. The only particular difference that we have established so far is that we are calling an additional provider azure-native config for a different subscription creating DNS Records in our hub subscription. It may be the case that the pulumi provider only initializes the additional provider config using the original idToken which is shortlived.

Output of pulumi about

pulumi 3.178.0 azure-native 3.4.0

Additional context

We would like to continue using OIDC based authentication as we have streamlined our pipelines to not use any secrets, so if you have additional workaround we would highly appreciate it.

Contributing

Vote on this issue by adding a 👍 reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

AndreasMWalter avatar Aug 15 '25 09:08 AndreasMWalter

Hi @AndreasMWalter, good news! I believe this is in progress here https://github.com/pulumi/pulumi-azure-native/pull/4242

blampe avatar Aug 21 '25 18:08 blampe

Unfortunately #4242 will not address this issue, which I read as a feature request for the Azure DevOps Pipeline OIDC authentication method, akin to this:

  • https://github.com/hashicorp/go-azure-sdk/pull/1139
  • https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_oidc

EronWright avatar Aug 21 '25 20:08 EronWright

@AndreasMWalter a new release of azure-native (3.8) is out with support for a new mode called DefaultAzureCredential. https://www.pulumi.com/blog/azure-native-defaultazurecredential-and-private-cloud/

I'm wondering whether DefaultAzureCredential would be effective here.

EronWright avatar Sep 05 '25 15:09 EronWright

@AndreasMWalter a new release of azure-native (3.8) is out with support for a new mode called DefaultAzureCredential. https://www.pulumi.com/blog/azure-native-defaultazurecredential-and-private-cloud/

I'm wondering whether DefaultAzureCredential would be effective here.

I cannot check this before September 15th, however I don't believe so as DefaultAzureCredential only has a limited subset of credentials it uses in a specific order

AndreasMWalter avatar Sep 05 '25 18:09 AndreasMWalter

@AndreasMWalter I believe that the WorkloadIdentityCredential will activate in the Pipelines environment.

EronWright avatar Sep 10 '25 17:09 EronWright

Ahoi, we are trying do a quick test this week, I will report back

AndreasMWalter avatar Sep 16 '25 07:09 AndreasMWalter

@EronWright

I'm wondering whether DefaultAzureCredential would be effective here.

AFAICT this cannot possibly work. There is no path in the SDK code leading from DefaultAzureCredential to AzurePipelineCredential, which is the piece of code that provides support for OIDC request URLs. WorkloadIdentityCredential in particular only looks at AZURE_FEDERATED_TOKEN_FILE.

codethief avatar Sep 16 '25 14:09 codethief

If I may make a suggestion regarding how to fix the present issue:

  • Add support for the azure-sdk-for-go's AzurePipelinesCredential, similarly to https://github.com/go-acme/lego/issues/2620 , and expose it through some useAzurePipelinesCredential config option.
  • Rename the existing useOidc option to something like useGithubOidc, since it is Github-specific and very confusing otherwise. Alternatively, add a new parameter oidcProvider: "github" | "azdo" | … to make choosing the OIDC provider explicit, or at the very least document that useOidc is meant exclusively for Github.

Note that AzurePipelinesCredential does some non-trivial things like appending additional Azure-specific parameters to the OIDC request URL, including the service connection ID provided by the user and an oidcAPIVersion, and – as mentioned before – setting the HTTP method to POST. Similarly, the current implementation for Github does other non-trivial things, like using HTTP GET and setting various headers.

So a generic OIDC authentication method likely won't be possible unless one wants to leave it to the user to configure all those knobs correctly. It'd probably make more sense to follow in the footsteps of go-acme/lego which uses azure-sdk-for-go's AzurePipelinesCredential (see above), and of terraform-azurerm-provider, which uses Hashicorp's go-azure-sdk's ADOPipelineOIDCAuthorizer.

codethief avatar Sep 18 '25 16:09 codethief

Thanks @codethief. I like the idea of adding explicit support via useAzurePipelinesCredential, as this seems to be how other tools have solved it. Meanwhile, https://github.com/Azure/azure-sdk-for-go/issues/23181 tracks the upstream task of adding Azure Pipelines support to the DefaultAzureCredential.

EronWright avatar Sep 18 '25 22:09 EronWright