Purpose of this script
Create a managed identity trusted by GitHub Actions through OIDC, and grant it permission to deploy to the API web app — the hand-run half of what Terraform now does more completely.
Everything explained on the github_oidc.tf tutorial page about why OIDC is used instead of a stored password, and why the permission is scoped to one web app instead of the whole subscription, applies here unchanged — this script just issues the equivalent az identity and az role assignment commands directly, instead of declaring the same outcome in a .tf file.
Before you run it prerequisites
- The target web app must already exist — run az_create_resources first.
- You're authenticated as an identity with permission to create managed identities and assign roles on the resource group / web app.
- The GitHub repository and environment name you're setting this up for — defaults match this project's own repository and a Development environment.
Walking through the script PowerShell version
$encodedEnvironment = $GitHubEnvironment.Replace(':', '%3A')
$FederatedSubject = "repo:gdzierzon@9723466/cohort-d-dzierzong-metals-simple-deploy@1361521533:environment:${encodedEnvironment}"
The exact same computed value as main.tf's local.oidc_subject — a fixed string identifying “GitHub Actions, running in this exact repository, in this exact environment.” For any repository other than this project's own, you have to pass -FederatedSubject explicitly with the value GitHub's own azure/login action prints in its log — the script can't safely guess another repository's immutable owner/repo IDs.
$app = Invoke-Az webapp show --resource-group $resourceGroup --name $appName `
--query '{id:id,location:location}' --output json | ConvertFrom-Json
Needs the web app's ID (to scope the role assignment) and location (to create the identity in the same region) — both read directly from the resource that az_create_resources already created.
$identity = Invoke-Az identity create --resource-group $resourceGroup --name $identityName `
--location $app.location --output json | ConvertFrom-Json
The manual equivalent of github_oidc.tf's azurerm_user_assigned_identity — an empty identity, with no permissions yet.
Invoke-Az identity federated-credential create `
--resource-group $resourceGroup --identity-name $identityName --name github-development `
--issuer 'https://token.actions.githubusercontent.com' --subject $FederatedSubject `
--audiences 'api://AzureADTokenExchange'
Same issuer, audience, and subject concepts covered on the github_oidc.tf page — this is the trust rule itself, deciding exactly which GitHub Actions tokens Azure will accept.
Invoke-Az role assignment create `
--assignee-object-id $identity.principalId --assignee-principal-type ServicePrincipal `
--role 'Website Contributor' --scope $app.id
The same least-privilege pattern as github_oidc.tf's role assignments: Website Contributor, scoped to $app.id — this one web app — not the resource group and not the subscription.
One assignment, on the API app only. That was sufficient when the API was the only deployable thing; it no longer covers everything the workflows need. See the gap below.
Write-Host "AZURE_CLIENT_ID = $($identity.clientId)" Write-Host "AZURE_TENANT_ID = $($identity.tenantId)" Write-Host "AZURE_SUBSCRIPTION_ID = $subscriptionId"
The same three identifiers Terraform's outputs.tf publishes as its github_secrets output — ready to paste into the repository's Development environment secrets.
The gap versus Terraform read this before relying on it
This script predates the move to containers and hasn't been extended to match. It still grants exactly one role on exactly one app, while github_oidc.tf now grants three assignments — because deploying containers needs more than deploying code did.
| Role assignment | This script | Terraform | Needed for |
|---|---|---|---|
| Website Contributor on the API app | Yes | Yes | Configuring and restarting the API. |
| Website Contributor on the UI app | No | Yes | Configuring and restarting the UI. |
| Contributor on the registry | No | Yes | az acr build — building and pushing either image. |
Repairing a broken credential -UpdateCredentialOnly
If the identity and role assignment already exist, but the trusted subject was wrong (a common cause of authentication failures), pass -UpdateCredentialOnly to update just the federated credential's subject, without recreating the identity or re-granting the role. This trades the safety of Terraform's plan/apply cycle for a narrower, faster fix — there's no equivalent single-purpose “just fix the subject” command in the Terraform workflow; you'd change the relevant variable and run terraform apply again instead.
Common errors and how to fix them
| What you see | Likely cause | How to fix it |
|---|---|---|
| GitHub Actions fails to authenticate to Azure (“AADSTS70021” or similar) | The federated credential's subject doesn't exactly match what GitHub actually sent — often because the GitHub environment name, or an owner/repo ID, differs from what was assumed. | Copy the exact subject claim from the failed azure/login step's log, then rerun this script with -FederatedSubject (or -UpdateCredentialOnly if the identity already exists). |
| “For another repository, pass -FederatedSubject...” | You're running this against a different repository than the project's own default, without supplying the subject explicitly. | Get the exact subject from a GitHub Actions log for that repository, and pass it with -FederatedSubject. |
| The very next deployment run still fails right after this script succeeds | Azure role assignment and federated identity changes aren't always instantly visible everywhere. | Wait a few minutes before rerunning the failed GitHub Actions job. |
| “ResourceNotFound” looking up the web app | az_create_resources hasn't been run yet, or used a different -UserName. | Run az_create_resources first with the same -UserName. |