Cloud can be tricky sometimes. Find out what scenarios we've ran into that are worth being mentioned and explained.
The diagram illustrates the orchestration process for configuring workload identity federation through Azure Bicep using OpenID Connect. The workflow follows these steps:
YAML
steps:
- task: AzureCLI@2
inputs:
azureSubscription: ''
scriptType: 'pscore'
scriptLocation: 'inlineScript'
inlineScript: "az deployment group create --template-file--parameters --resource-group "
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
This loads the token as an environment variable, which is later retrieved in the Bicep template using:
Bicep
param parAzureDevOpsSystemAccessToken = readEnvironmentVariable('SYSTEM_ACCESSTOKEN')
2. Configuring Permissions in Azure DevOps
@secure()
param parAzureDevOpsToken string
param parOrgName string
param parProjectName string
param parServiceConnName string
param parLocation string = 'westeurope'
param parDeploymentTime string = utcNow('yyyy-MM-dd-HH-mm-ss')
// Get Azure DevOps Information
resource resDevOpsInfo 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'get-devops-details'
location: parLocation
kind: 'AzurePowerShell'
properties: {
azPowerShellVersion: '11.0'
retentionInterval: 'P1D'
forceUpdateTag: parDeploymentTime
environmentVariables: [
{
name: 'DevOpsToken'
secureValue: parAzureDevOpsToken
}
]
arguments: '-OrgName ${parOrgName} -ProjectName ${parProjectName} -ServiceConnName ${parServiceConnName}'
scriptContent: loadTextContent('scripts/get-devops-info.ps1')
}
}
// Create Managed Identity
resource resManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: parServiceConnName
location: parLocation
resource resFederation 'federatedIdentityCredentials' = {
name: 'federation-config'
properties: {
issuer: resDevOpsInfo.properties.outputs.issuer
subject: resDevOpsInfo.properties.outputs.subjectIdentifier
audiences: ['api://AzureADTokenExchange']
}
}
}
// Configure Service Connection
} resource resConfigureConnection 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'setup-service-connection'
location: parLocation
kind: 'AzurePowerShell'
properties: {
azPowerShellVersion: '11.0'
retentionInterval: 'P1D'
forceUpdateTag: parDeploymentTime
environmentVariables: [
{
name: 'DevOpsToken'
secureValue: parAzureDevOpsToken
}
]
arguments: '-OrgName ${parOrgName} -ProjectName ${parProjectName} -ServiceConnName ${parServiceConnName} -TenantId ${tenant().tenantId} -SubId ${subscription().subscriptionId} -SubName ${subscription().displayName} -ManagedIdentityId ${resManagedIdentity.properties.clientId}'
scriptContent: loadTextContent('scripts/configure-connection.ps1')
}
PowerShell Scripts
param (
[string] $OrgName,
[string] $ProjectName,
[string] $ServiceConnName
)
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($env:DevOpsToken)"))
$headers = @{ Authorization = "Basic $auth" }
$orgInfo = Invoke-RestMethod -Uri "https://dev.azure.com/$OrgName/_apis/connectiondata?api-version=5.0-preview.1" -Headers $headers
$orgId = $orgInfo.instanceId
$deploymentScriptOutputs = @{
issuer = "https://vstoken.dev.azure.com/$orgId"
subjectIdentifier = "sc://$OrgName/$ProjectName/$ServiceConnName"
}
param (
[string] $OrgName,
[string] $ProjectName,
[string] $ServiceConnName,
[string] $TenantId,
[string] $SubId,
[string] $SubName,
[string] $ManagedIdentityId
)
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($env:DevOpsToken)"))
$headers = @{ Authorization = "Basic $auth" }
# Create service connection configuration
$config = @{
authorization = @{
parameters = @{
serviceprincipalid = $ManagedIdentityId
tenantid = $TenantId
}
scheme = "WorkloadIdentityFederation"
}
data = @{
environment = "AzureCloud"
scopeLevel = "Subscription"
subscriptionId = $SubId
subscriptionName = $SubName
}
name = $ServiceConnName
type = "AzureRM"
url = "https://management.azure.com/"
}
# Deploy service connection
$endpoint = Invoke-RestMethod -Uri "https://dev.azure.com/$OrgName/_apis/serviceendpoint/endpoints?api-version=7.1-preview.4" -Headers $headers -Method Post -Body ($config | ConvertTo-Json -Depth 10)
# Configure pipeline permissions
$permissions = @{
allPipelines = @{ authorized = $true }
pipelines = @()
resource = @{
id = $endpoint.id
type = "endpoint"
}
}
Invoke-RestMethod -Uri "https://dev.azure.com/$OrgName/$ProjectName/_apis/pipelines/pipelinePermissions/endpoint/$($endpoint.id)?api-version=7.1-preview.1" -Headers $headers -Method Patch -Body ($permissions | ConvertTo-Json)