TutorialsDeploymentManual › az_setup_github_oidc

Manual Deployment · Script 3 of 4

az_setup_github_oidc

Creates a passwordless Azure identity that GitHub Actions can use to deploy to the API web app — the manual, hand-run version of what github_oidc.tf automates. Same concepts, same security model, one az command at a time instead of a declared resource. Note that it grants less than the Terraform version does — see the gap below before relying on it for container deployments.

Codey the Sr Developer, standing with a pointer, ready to walk through the guide
Files → az_setup_github_oidc.ps1 · .sh Requires first → az_create_resources Terraform equivalent → github_oidc.tf

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.

Codey inspecting something with a magnifying glass
same trust model as github_oidc.tf

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

1. Build the exact subject GitHub will present$FederatedSubject
$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.

2. Look up the web appaz webapp show
$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.

3. Create the identityaz identity create
$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.

4. Trust GitHub's tokens for that exact subjectaz identity federated-credential create
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.

5. Grant exactly enough permissionaz role assignment create
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.

6. Print what to paste into GitHubconsole output
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.

Codey holding a bug-hunting net
a known, deliberate gap
Role assignmentThis scriptTerraformNeeded for
Website Contributor on the API appYesYesConfiguring and restarting the API.
Website Contributor on the UI appNoYesConfiguring and restarting the UI.
Contributor on the registryNoYesaz acr build — building and pushing either image.

In practice this means an identity created by this script alone will fail the deploy workflows with AuthorizationFailed. Either add the two missing assignments by hand with az role assignment create, or let Terraform manage the identity — which is what this project actually does. The reasoning behind each role is on the Identity & permissions page.

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 seeLikely causeHow 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 succeedsAzure 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 appaz_create_resources hasn't been run yet, or used a different -UserName.Run az_create_resources first with the same -UserName.
Codey giving a thumbs up

GitHub can deploy on its own now — last script, cleaning everything back up.