Skip to main content

Repository onboarding

This page helps you connect a GitHub repository to Azure so that Git-Ape's CI/CD workflows can deploy resources automatically. By the end you will have OIDC federation, RBAC roles, and GitHub environments configured.

warning

EXPERIMENTAL ONLY — This onboarding flow is for testing and evaluation. Do not use Git-Ape in production environments. Validate all generated configuration manually before any real deployment.

Choose your onboarding path​

Git-Ape can automate the entire setup for you, or you can run each step manually.

PathWhen to choose it
AutomatedYou want the fastest path. Copilot Chat runs every command for you.
ManualYou want to understand each component, or your organization requires manual approval of identity and RBAC changes.

Both paths produce the same result: an Entra ID App Registration with OIDC federated credentials, RBAC role assignments, and GitHub environments with the required secrets.

Choose single or multi-environment mode​

Before onboarding, decide how many Azure subscriptions you need.

Single environmentMulti-environment
GitHub environmentsazure-deploy, azure-destroyazure-deploy-dev, azure-deploy-staging, azure-deploy-prod, azure-destroy
Secrets scopeRepository-levelEnvironment-level per stage
SubscriptionsOneOne per stage
Federated credentials44 + one per extra stage
Best forPersonal projects, demos, PoCsTeams, staging gates, production isolation
Not sure?

Start with single environment. You can switch to multi-environment later by adding more federated credentials and GitHub environments.

How OIDC authentication works​

Git-Ape uses OpenID Connect (OIDC) so that no client secrets are stored in your repository. GitHub mints a short-lived token at workflow runtime, Entra ID verifies it against a trust relationship you configure once, and returns an Azure access token.

The trust chain you create during onboarding:


Automated onboarding​

Run one of these commands in Copilot Chat:

@Git-Ape Onboarding onboard this repository

or:

/git-ape-onboarding

The skill collects five inputs (or uses sensible defaults):

  1. GitHub repository URL — for example, https://github.com/your-org/your-repo
  2. Entra ID App Registration name — for example, sp-git-ape-your-repo
  3. Mode — single or multi-environment
  4. Azure subscription(s) — defaults to your current az subscription
  5. RBAC role(s) — Contributor (default) or Contributor + User Access Administrator

Example: single environment​

/git-ape-onboarding onboard https://github.com/your-org/your-repo on subscription 00000000-... with Contributor

Example: multi-environment​

/git-ape-onboarding onboard https://github.com/your-org/your-repo with dev on 11111111-... as Contributor, staging on 22222222-... as Contributor, prod on 33333333-... as Contributor+UserAccessAdministrator

After the skill finishes, skip to Verify your setup.


Manual setup​

Follow these steps if you want to run each command yourself or need to understand what the automated flow does.

Prerequisites​

ToolMinimum versionPurpose
Azure CLI (az)2.50+Entra ID, RBAC, OIDC
GitHub CLI (gh)2.0+Secrets, environments
jq1.6+JSON parsing
tip

Run /prereq-check in Copilot Chat to validate tools and auth sessions automatically.

Install commands by platform

macOS:

brew install azure-cli gh jq

Ubuntu / Debian:

curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# GitHub CLI — see https://github.com/cli/cli/blob/trunk/docs/install_linux.md
sudo apt-get install -y jq

Windows (PowerShell):

winget install Microsoft.AzureCLI
winget install GitHub.cli
winget install jqlang.jq

Sign in to both:

az login # needs Owner or User Access Administrator on the subscription
gh auth login # needs admin access to the target repository

Step 1: Create an Entra ID App Registration​

This creates the identity that GitHub Actions will use.

SP_NAME="sp-git-ape-your-repo"

CLIENT_ID=$(az ad app create --display-name "$SP_NAME" --query appId -o tsv)
echo "Client ID: $CLIENT_ID"

az ad sp create --id "$CLIENT_ID"

TENANT_ID=$(az account show --query tenantId -o tsv)
echo "Tenant ID: $TENANT_ID"

Step 2: Add OIDC federated credentials​

These tell Entra ID which GitHub tokens to trust.

OBJECT_ID=$(az ad app show --id "$CLIENT_ID" --query id -o tsv)
REPO="your-org/your-repo"

# Detect customized OIDC subjects (some orgs override the default format)
USE_DEFAULT=$(gh api "orgs/${REPO%%/*}/actions/oidc/customization/sub" --jq '.use_default' 2>/dev/null || echo true)

if [[ "$USE_DEFAULT" == "false" ]]; then
REPO_ID=$(gh api "repos/$REPO" --jq '.id')
OWNER_ID=$(gh api "repos/$REPO" --jq '.owner.id')
OIDC_PREFIX="repository_owner_id:${OWNER_ID}:repository_id:${REPO_ID}"
else
OIDC_PREFIX="repo:$REPO"
fi

Create the credentials. You need at least four — main branch, pull requests, deploy environment, and destroy environment:

# Main branch (merge-triggered deployments)
az ad app federated-credential create --id "$OBJECT_ID" --parameters '{
"name": "fc-main-branch",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "'"$OIDC_PREFIX"':ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"]
}'

# Pull requests (plan validation)
az ad app federated-credential create --id "$OBJECT_ID" --parameters '{
"name": "fc-pull-request",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "'"$OIDC_PREFIX"':pull_request",
"audiences": ["api://AzureADTokenExchange"]
}'
Single environment: deploy + destroy credentials
az ad app federated-credential create --id "$OBJECT_ID" --parameters '{
"name": "fc-env-deploy",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "'"$OIDC_PREFIX"':environment:azure-deploy",
"audiences": ["api://AzureADTokenExchange"]
}'

az ad app federated-credential create --id "$OBJECT_ID" --parameters '{
"name": "fc-env-destroy",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "'"$OIDC_PREFIX"':environment:azure-destroy",
"audiences": ["api://AzureADTokenExchange"]
}'
Multi-environment: per-stage deploy + destroy credentials
for ENV in dev staging prod; do
az ad app federated-credential create --id "$OBJECT_ID" --parameters '{
"name": "fc-env-deploy-'"$ENV"'",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "'"$OIDC_PREFIX"':environment:azure-deploy-'"$ENV"'",
"audiences": ["api://AzureADTokenExchange"]
}'
done

az ad app federated-credential create --id "$OBJECT_ID" --parameters '{
"name": "fc-env-destroy",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "'"$OIDC_PREFIX"':environment:azure-destroy",
"audiences": ["api://AzureADTokenExchange"]
}'

Verify:

az ad app federated-credential list --id "$OBJECT_ID" \
--query "[].{name:name, subject:subject}" -o table

Step 3: Assign RBAC roles​

Grant the service principal permissions on your subscription(s).

SUBSCRIPTION_ID=$(az account show --query id -o tsv)
SP_OBJECT_ID=$(az ad sp show --id "$CLIENT_ID" --query id -o tsv)

az role assignment create \
--assignee-object-id "$SP_OBJECT_ID" \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope "/subscriptions/$SUBSCRIPTION_ID"
Do I need User Access Administrator?

Only if your ARM templates include RBAC role assignments — for example, granting a managed identity access to a storage account. If you are unsure, skip it for now. Deployments will tell you if the role is missing.

Multi-environment: assign roles per subscription
SP_OBJECT_ID=$(az ad sp show --id "$CLIENT_ID" --query id -o tsv)

# Dev — Contributor only
az role assignment create \
--assignee-object-id "$SP_OBJECT_ID" \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope "/subscriptions/$DEV_SUBSCRIPTION_ID"

# Staging — Contributor only
az role assignment create \
--assignee-object-id "$SP_OBJECT_ID" \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope "/subscriptions/$STAGING_SUBSCRIPTION_ID"

# Production — Contributor + User Access Administrator
az role assignment create \
--assignee-object-id "$SP_OBJECT_ID" \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope "/subscriptions/$PROD_SUBSCRIPTION_ID"

az role assignment create \
--assignee-object-id "$SP_OBJECT_ID" \
--assignee-principal-type ServicePrincipal \
--role "User Access Administrator" \
--scope "/subscriptions/$PROD_SUBSCRIPTION_ID"

Step 4: Configure GitHub repository​

Set secrets​

These are identifiers, not actual credentials — OIDC means no secrets are stored.

Single environment
REPO="your-org/your-repo"
echo "$CLIENT_ID" | gh secret set AZURE_CLIENT_ID -R "$REPO"
echo "$TENANT_ID" | gh secret set AZURE_TENANT_ID -R "$REPO"
echo "$SUBSCRIPTION_ID" | gh secret set AZURE_SUBSCRIPTION_ID -R "$REPO"
Multi-environment

Set shared values at the repo level, then the subscription per environment:

REPO="your-org/your-repo"
echo "$CLIENT_ID" | gh secret set AZURE_CLIENT_ID -R "$REPO"
echo "$TENANT_ID" | gh secret set AZURE_TENANT_ID -R "$REPO"

for ENV in dev staging prod; do
# Use the right subscription variable for each stage
gh secret set AZURE_CLIENT_ID --repo "$REPO" --env "azure-deploy-$ENV" --body "$CLIENT_ID"
gh secret set AZURE_TENANT_ID --repo "$REPO" --env "azure-deploy-$ENV" --body "$TENANT_ID"
gh secret set AZURE_SUBSCRIPTION_ID --repo "$REPO" --env "azure-deploy-$ENV" --body "${ENV}_SUBSCRIPTION_ID_VALUE"
done

# Destroy environment
gh secret set AZURE_CLIENT_ID --repo "$REPO" --env "azure-destroy" --body "$CLIENT_ID"
gh secret set AZURE_TENANT_ID --repo "$REPO" --env "azure-destroy" --body "$TENANT_ID"
gh secret set AZURE_SUBSCRIPTION_ID --repo "$REPO" --env "azure-destroy" --body "$DEV_SUBSCRIPTION_ID"

Create GitHub environments​

Single environment
REPO="your-org/your-repo"

# azure-deploy — restricted to main branch
gh api -X PUT "repos/$REPO/environments/azure-deploy" --input - <<'EOF'
{"deployment_branch_policy":{"protected_branches":false,"custom_branch_policies":true}}
EOF
gh api -X POST "repos/$REPO/environments/azure-deploy/deployment-branch-policies" \
--input - <<'EOF'
{"name":"main","type":"branch"}
EOF

# azure-destroy
gh api -X PUT "repos/$REPO/environments/azure-destroy" --input - <<'EOF'
{"deployment_branch_policy":null}
EOF
Multi-environment
REPO="your-org/your-repo"

for ENV in dev staging prod; do
gh api -X PUT "repos/$REPO/environments/azure-deploy-$ENV" --input - <<'EOF'
{"deployment_branch_policy":{"protected_branches":false,"custom_branch_policies":true}}
EOF
gh api -X POST "repos/$REPO/environments/azure-deploy-$ENV/deployment-branch-policies" \
--input - <<'EOF'
{"name":"main","type":"branch"}
EOF
done

gh api -X PUT "repos/$REPO/environments/azure-destroy" --input - <<'EOF'
{"deployment_branch_policy":null}
EOF
Add required reviewers for production

Go to Settings → Environments → azure-deploy (or azure-deploy-prod in multi-env mode), check Required reviewers, and add your team. This prevents unreviewed deployments from reaching production.

Step 5: Copy Git-Ape workflows​

# If you haven't already
git clone https://github.com/Azure/git-ape.git /tmp/git-ape

cp /tmp/git-ape/.github/workflows/git-ape-*.yml your-repo/.github/workflows/
cd your-repo
git add .github/workflows/
git commit -m "feat: add Git-Ape deployment workflows"
git push

This adds four workflows:

WorkflowTriggerPurpose
git-ape-plan.ymlPR with template changesValidate, what-if, cost estimate
git-ape-deploy.ymlMerge to main or /deploy commentARM deployment
git-ape-destroy.ymlPR merge with destroy-requested statusDelete resource group
git-ape-verify.ymlManual dispatchVerify OIDC and RBAC health

Verify your setup​

Create a test deployment to confirm the pipeline works end-to-end:

mkdir -p .azure/deployments/deploy-test

cat > .azure/deployments/deploy-test/template.json <<'EOF'
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {"location": {"type": "string", "defaultValue": "eastus"}},
"resources": []
}
EOF

cat > .azure/deployments/deploy-test/parameters.json <<'EOF'
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {"location": {"value": "eastus"}}
}
EOF

git checkout -b test/git-ape-onboarding
git add .azure/deployments/deploy-test/
git commit -m "test: verify git-ape pipeline"
git push -u origin test/git-ape-onboarding
gh pr create --title "Test: Git-Ape onboarding" --body "Verify the OIDC pipeline works."

If the Git-Ape: Plan workflow runs and succeeds on the PR, your setup is complete.


Troubleshooting​

"AADSTS700016: Application not found"

The federated credential subject does not match the workflow token. Common causes:

  • Repository name is case-sensitive in the subject field.
  • Missing pull_request subject for PR-triggered workflows.
  • Missing environment:azure-deploy subject for deploy jobs.

Check: az ad app federated-credential list --id "$OBJECT_ID" -o table

"AuthorizationFailed" during deployment

The service principal lacks permissions. Check: az role assignment list --assignee "$SP_OBJECT_ID" -o table

Ensure Contributor is assigned at the subscription scope.

GitHub environment not created

Environment creation requires admin access to the repository. Ask a repo admin to create the environments manually via Settings → Environments.


Security considerations​

AspectImplementation
No stored secretsOIDC federated identity — no client secrets or certificates
Scoped accessFederated credentials are scoped per repo + branch/environment
Least privilegeContributor role by default; add User Access Administrator only when needed
Environment gatesDeploy environments restricted to main branch
Destructive protectionazure-destroy can require manual approval
Audit trailAll deployments logged in state.json with actor, timestamp, run URL