Skip to content

Commit 20a2942

Browse files
authored
Fix production container image deployment by granting ACR pull access via Bicep infrastructure (#711)
### Summary & motivation Fix an issue where container images were not deployed to production due to incorrect federated credentials when attempting to pull images from the staging container registry. The previous approach relied on GitHub Actions federated credentials, which failed because the production workflow was not using the correct federated credential to access staging. To resolve this, Bicep infrastructure has been updated to grant the production service principal ACR pull permissions on the staging container registry during infrastructure deployment. This ensures that production can securely pull images from staging without requiring federated identity access. This change makes the GitHub workflow much cleaner, as there is no longer a need for production workflows to log in to staging. However, this change introduced a new challenge: the GitHub environment variable `PRODUCTION_SERVICE_PRINCIPAL_ID` could not be used for role assignment. The required Object ID is not accessible from GitHub Actions because Microsoft Entra ID cannot be queried from the workflow. To work around this, a new `PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID` environment variable has been introduced, and the `configure-continuous-deployments` CLI has been updated to create this. ### Downstream projects To support this change, you must create a new GitHub variable named `PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID` before merging this into `main`. #### How to obtain the required object ID: 1. **Preferred method**: Re-run `pp configure-continuous-deployments` using the Developer CLI, which will recreate all your GitHub variables (with the same values) and add the new `PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID`. 2. **Manual method**: If re-running the CLI is not feasible, retrieve the object ID manually: - Go to Azure Portal. - Navigate to **Microsoft Entra ID > Enterprise Applications** (not to be confused with App Registrations). - Search for `GitHub - Production - your-github-org/your-github-repo`. - Copy the **Object ID** and create a new `PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID` GitHub variable with this value. If this is not set or set with wrong ID, deployment of staging infrastructure will fail with errors such as `"PrincipalTypeNotSupported"` or `"InvalidPrincipalId"`. ### Checklist - [x] I have added tests, or done manual regression tests - [x] I have updated the documentation, if necessary
2 parents 1bf1e2e + a10227a commit 20a2942

File tree

6 files changed

+30
-18
lines changed

6 files changed

+30
-18
lines changed

.github/workflows/_deploy-container.yml

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -114,21 +114,8 @@ jobs:
114114
STAGING_SUBSCRIPTION_ID: ${{ vars.STAGING_SUBSCRIPTION_ID }}
115115

116116
steps:
117-
- name: Login to Azure (Staging)
118-
uses: azure/login@v2
119-
with:
120-
client-id: ${{ env.STAGING_SERVICE_PRINCIPAL_ID }}
121-
tenant-id: ${{ env.TENANT_ID }}
122-
subscription-id: ${{ env.STAGING_SUBSCRIPTION_ID }}
123117

124-
- name: Get Access Token for Staging Azure Subscription
125-
id: staging_tokens
126-
run: |
127-
STAGING_TOKEN=$(az account get-access-token --resource https://management.azure.com --query accessToken -o tsv)
128-
echo "::add-mask::$STAGING_TOKEN"
129-
echo "access_token=$STAGING_TOKEN" >> $GITHUB_OUTPUT
130-
131-
- name: Login to Azure (Production)
118+
- name: Login to Azure
132119
uses: azure/login@v2
133120
with:
134121
client-id: ${{ env.SERVICE_PRINCIPAL_ID }}
@@ -144,7 +131,6 @@ jobs:
144131
--name ${{ env.UNIQUE_PREFIX }}${{ env.ENVIRONMENT }} \
145132
--source ${{ env.UNIQUE_PREFIX }}${{ env.STAGING_ENVIRONMENT }}.azurecr.io/${{ inputs.image_name }}:${{ inputs.version }} \
146133
--image ${{ inputs.image_name }}:${{ inputs.version }} \
147-
--password ${{ steps.staging_tokens.outputs.access_token }} \
148134
--force
149135
150136
- name: Deploy Container
@@ -170,4 +156,4 @@ jobs:
170156
echo "($i) Waiting for revision to become active. Running state: $RUNNING_STATUS"
171157
done
172158
echo "New revision did not become active in time. Running state: $RUNNING_STATUS"
173-
exit 1
159+
exit 1

.github/workflows/_deploy-infrastructure.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ on:
4242
deployment_enabled:
4343
required: true
4444
type: string
45+
production_service_principal_object_id:
46+
required: false
47+
type: string
48+
default: "-"
4549

4650
jobs:
4751
plan:
@@ -67,7 +71,7 @@ jobs:
6771

6872
- name: Plan Shared Environment Resources
6973
if: ${{ inputs.include_shared_environment_resources == true }}
70-
run: bash ./cloud-infrastructure/environment/deploy-environment.sh ${{ inputs.unique_prefix }} ${{ inputs.azure_environment }} ${{ inputs.shared_location }} --plan
74+
run: bash ./cloud-infrastructure/environment/deploy-environment.sh ${{ inputs.unique_prefix }} ${{ inputs.azure_environment }} ${{ inputs.shared_location }} ${{ inputs.production_service_principal_object_id }} --plan
7175

7276
- name: Plan Cluster Resources
7377
id: deploy_cluster
@@ -99,7 +103,7 @@ jobs:
99103

100104
- name: Deploy Shared Environment Resources
101105
if: ${{ inputs.include_shared_environment_resources == true }}
102-
run: bash ./cloud-infrastructure/environment/deploy-environment.sh ${{ inputs.unique_prefix }} ${{ inputs.azure_environment }} ${{ inputs.shared_location }} --apply
106+
run: bash ./cloud-infrastructure/environment/deploy-environment.sh ${{ inputs.unique_prefix }} ${{ inputs.azure_environment }} ${{ inputs.shared_location }} ${{ inputs.production_service_principal_object_id }} --apply
103107

104108
- name: Deploy Cluster Resources
105109
id: deploy_cluster

.github/workflows/cloud-infrastructure.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
tenant_id: ${{ vars.TENANT_ID }}
4242
subscription_id: ${{ vars.STAGING_SUBSCRIPTION_ID }}
4343
deployment_enabled: ${{ vars.STAGING_CLUSTER_ENABLED }}
44+
production_service_principal_object_id: ${{ vars.PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID }}
4445

4546
prod1:
4647
name: Production

cloud-infrastructure/environment/deploy-environment.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@
33
UNIQUE_PREFIX=$1
44
ENVIRONMENT=$2
55
LOCATION_SHARED=$3
6+
PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID=$4
67

78
RESOURCE_GROUP_NAME=$UNIQUE_PREFIX-$ENVIRONMENT
89
CONTAINER_REGISTRY_NAME=$UNIQUE_PREFIX$ENVIRONMENT
910
CURRENT_DATE=$(date +'%Y-%m-%dT%H-%M')
1011
DEPLOYMENT_COMMAND="az deployment sub create"
1112
DEPLOYMENT_PARAMETERS="-l $LOCATION_SHARED -n $CURRENT_DATE-$UNIQUE_PREFIX-$ENVIRONMENT --output table -f ./main-environment.bicep -p resourceGroupName=$RESOURCE_GROUP_NAME environment=$ENVIRONMENT containerRegistryName=$CONTAINER_REGISTRY_NAME"
1213

14+
# Add production service principal object ID parameter if provided for ACR Pull role assignment to staging Container Registry
15+
if [[ "$PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID" != "-" ]]; then
16+
echo "Using production service principal object ID: $PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID"
17+
DEPLOYMENT_PARAMETERS="$DEPLOYMENT_PARAMETERS productionServicePrincipalObjectId=$PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID"
18+
fi
19+
1320
cd "$(dirname "${BASH_SOURCE[0]}")"
1421
. ../deploy.sh
1522

cloud-infrastructure/environment/main-environment.bicep

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ param location string = deployment().location
44
param resourceGroupName string
55
param environment string
66
param containerRegistryName string
7+
param productionServicePrincipalObjectId string = ''
78

89
var tags = { environment: environment, 'managed-by': 'bicep' }
910

@@ -23,6 +24,16 @@ module containerRegistry '../modules/container-registry.bicep' = {
2324
}
2425
}
2526

27+
// Grant production service principal ACR Pull access to registry if specified
28+
module productionServicePrincipalAcrPull '../modules/role-assignments-container-registry-acr-pull.bicep' = if (!empty(productionServicePrincipalObjectId)) {
29+
name: '${resourceGroupName}-production-sp-acr-pull'
30+
scope: resourceGroup(environmentResourceGroup.name)
31+
params: {
32+
containerRegistryName: containerRegistryName
33+
principalId: productionServicePrincipalObjectId
34+
}
35+
}
36+
2637
module logAnalyticsWorkspace '../modules/log-analytics-workspace.bicep' = {
2738
name: '${resourceGroupName}-log-analytics-workspace'
2839
scope: resourceGroup(environmentResourceGroup.name)

developer-cli/Commands/ConfigureContinuousDeploymentsCommand.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ [bold]Please review planned changes before continuing.[/]
509509
* PRODUCTION_SUBSCRIPTION_ID: [blue]{Config.ProductionSubscription.Id}[/]
510510
* PRODUCTION_SHARED_LOCATION: [blue]{Config.ProductionLocation.SharedLocation}[/]
511511
* PRODUCTION_SERVICE_PRINCIPAL_ID: [blue]{productionServicePrincipal}[/]
512+
* PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID: [blue]{Config.ProductionSubscription.AppRegistration.ServicePrincipalObjectId}[/]
512513
* PRODUCTION_SQL_ADMIN_OBJECT_ID: [blue]{productionSqlAdminObject}[/]
513514
* PRODUCTION_DOMAIN_NAME: [blue]-[/] ([yellow]Manually changed this and triggered deployment to set up the domain[/])
514515
@@ -724,6 +725,7 @@ private void CreateGithubSecretsAndVariables()
724725

725726
SetGithubVariable(VariableNames.PRODUCTION_SUBSCRIPTION_ID, Config.ProductionSubscription.Id);
726727
SetGithubVariable(VariableNames.PRODUCTION_SERVICE_PRINCIPAL_ID, Config.ProductionSubscription.AppRegistration.ServicePrincipalId!);
728+
SetGithubVariable(VariableNames.PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID, Config.ProductionSubscription.AppRegistration.ServicePrincipalObjectId!);
727729
SetGithubVariable(VariableNames.PRODUCTION_SHARED_LOCATION, Config.ProductionLocation.SharedLocation);
728730
SetGithubVariable(VariableNames.PRODUCTION_SQL_ADMIN_OBJECT_ID, Config.ProductionSubscription.SqlAdminsGroup.ObjectId!);
729731
SetGithubVariable(VariableNames.PRODUCTION_DOMAIN_NAME, "-");
@@ -1058,6 +1060,7 @@ public enum VariableNames
10581060

10591061
PRODUCTION_SUBSCRIPTION_ID,
10601062
PRODUCTION_SERVICE_PRINCIPAL_ID,
1063+
PRODUCTION_SERVICE_PRINCIPAL_OBJECT_ID,
10611064
PRODUCTION_SHARED_LOCATION,
10621065
PRODUCTION_SQL_ADMIN_OBJECT_ID,
10631066
PRODUCTION_DOMAIN_NAME,

0 commit comments

Comments
 (0)