diff --git a/.azuredevops/pipelineTemplates/module.jobs.deploy.yml b/.azuredevops/pipelineTemplates/module.jobs.deploy.yml index 56faf474cd..28bf6c7497 100644 --- a/.azuredevops/pipelineTemplates/module.jobs.deploy.yml +++ b/.azuredevops/pipelineTemplates/module.jobs.deploy.yml @@ -176,10 +176,15 @@ jobs: @{ Name = "deploymentSpId"; Value = '$(DEPLOYMENT_SP_ID)' } ) | ForEach-Object { [PSCustomObject]$PSItem } + # Get additional Custom Parameter File Tokens from input + Write-Verbose 'Additional Custom Parameter File Tokens: ${{ deploymentBlock.customParameterFileTokens }}' -Verbose + $OtherCustomParameterFileTokens = '${{ deploymentBlock.customParameterFileTokens }}' | ConvertFrom-Json + # Construct Token Function Input $ConvertTokensInputs = @{ ParameterFilePath = Join-Path '$(parametersRepoRoot)' '${{ deploymentBlock.path }}' DefaultParameterFileTokens = $DefaultParameterFileTokens + OtherCustomParameterFileTokens = $OtherCustomParameterFileTokens LocalCustomParameterFileTokens = $Settings.parameterFileTokens.localTokens.tokens TokenPrefix = $Settings.parameterFileTokens.tokenPrefix TokenSuffix = $Settings.parameterFileTokens.tokenSuffix diff --git a/.azuredevops/platformPipelines/platform.dependencies.yml b/.azuredevops/platformPipelines/platform.dependencies.yml index 9abe3c0aea..b53d38722b 100644 --- a/.azuredevops/platformPipelines/platform.dependencies.yml +++ b/.azuredevops/platformPipelines/platform.dependencies.yml @@ -57,6 +57,29 @@ stages: - path: $(dependencyPath)/$(resourceType)/parameters/parameters.json templateFilePath: $(templateFilePath) displayName: User Assigned Identity + jobName: job_deploy_msi + - job: job_set_msi_id + displayName: Set msi principal ID output + dependsOn: + - job_deploy_msi + pool: + ${{ if eq(variables['vmImage'], '') }}: + name: $(poolName) + ${{ if eq(variables['poolName'], '') }}: + vmImage: $(vmImage) + variables: + deploymentOutput: $[ dependencies.job_deploy_msi.outputs['DeployModule.deploymentOutput'] ] + steps: + - task: PowerShell@2 + name: print_msi_prinId + inputs: + targetType: inline + pwsh: true + script: | + # Write-Verbose $(deploymentOutput) -Verbose + $msiPrincipalId = (ConvertFrom-Json '$(deploymentOutput)').msiPrincipalId + Write-Verbose "msiPrincipalId: $msiPrincipalId" -Verbose + Write-Output ('##vso[task.setvariable variable={0};isOutput=true]{1}' -f 'msiPrincipalId', $msiPrincipalId) - stage: deploy_pa displayName: Deploy policy assignment @@ -355,9 +378,11 @@ stages: - deploy_sa - deploy_evh - deploy_law + - deploy_msi variables: resourceType: 'Microsoft.RecoveryServices/vaults' templateFilePath: $(modulesPath)/$(resourceType)/deploy.bicep + msiPrincipalId: $[ stageDependencies.deploy_msi.job_set_msi_id.outputs['print_msi_prinId.msiPrincipalId'] ] jobs: - template: /.azuredevops/pipelineTemplates/module.jobs.deploy.yml parameters: @@ -365,6 +390,7 @@ stages: - path: $(dependencyPath)/$(resourceType)/parameters/parameters.json templateFilePath: $(templateFilePath) displayName: Default recovery services vault + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"$(msiPrincipalId)"}]' - stage: deploy_kv displayName: Deploy key vaults @@ -372,9 +398,11 @@ stages: - deploy_sa - deploy_evh - deploy_law + - deploy_msi variables: resourceType: 'Microsoft.KeyVault/vaults' templateFilePath: $(modulesPath)/$(resourceType)/deploy.bicep + msiPrincipalId: $[ stageDependencies.deploy_msi.job_set_msi_id.outputs['print_msi_prinId.msiPrincipalId'] ] jobs: - template: /.azuredevops/pipelineTemplates/module.jobs.deploy.yml parameters: @@ -383,14 +411,17 @@ stages: templateFilePath: $(templateFilePath) displayName: Default Key Vault jobName: default_kv + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"$(msiPrincipalId)"}]' - path: $(dependencyPath)/$(resourceType)/parameters/pe.parameters.json templateFilePath: $(templateFilePath) displayName: Private Endpoint Key Vault + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"$(msiPrincipalId)"}]' - ${{ if eq( parameters.deploySqlMiDependencies, true) }}: - path: $(dependencyPath)/$(resourceType)/parameters/sqlmi.parameters.json templateFilePath: $(templateFilePath) displayName: SQLMI key vault jobName: sqlmi_kv + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"$(msiPrincipalId)"}]' - job: displayName: Set key vault secrets keys and certificates dependsOn: @@ -558,8 +589,9 @@ stages: dependsOn: - deploy_msi variables: - resourceType: 'Microsoft.Authorization\roleAssignments' + resourceType: 'Microsoft.Authorization/roleAssignments' templateFilePath: $(modulesPath)/$(resourceType)/.bicep/nested_rbac_sub.bicep + msiPrincipalId: $[ stageDependencies.deploy_msi.job_set_msi_id.outputs['print_msi_prinId.msiPrincipalId'] ] jobs: - template: /.azuredevops/pipelineTemplates/module.jobs.deploy.yml parameters: @@ -567,6 +599,7 @@ stages: - path: $(dependencyPath)/$(resourceType)/parameters/parameters.json templateFilePath: $(templateFilePath) displayName: MSI Role Assignment + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"$(msiPrincipalId)"}]' - stage: deploy_vnet displayName: Deploy virtual networks diff --git a/.github/actions/templates/validateModuleDeployment/action.yml b/.github/actions/templates/validateModuleDeployment/action.yml index 8a6ae87cf1..7e87b017d1 100644 --- a/.github/actions/templates/validateModuleDeployment/action.yml +++ b/.github/actions/templates/validateModuleDeployment/action.yml @@ -20,6 +20,9 @@ inputs: managementGroupId: description: 'The managementGroupId to deploy to' required: false + customParameterFileTokens: + description: 'Additional parameter file token pairs in json format. e.g. [{"Name":"tokenName","Value":"tokenValue"}]' + required: false removeDeployment: description: 'Set "true" to set module up for removal' default: 'true' @@ -84,14 +87,17 @@ runs: @{ Name = 'managementGroupId'; Value = '${{ inputs.managementGroupId }}' } @{ Name = "tenantId"; Value = '${{ env.ARM_TENANT_ID }}' } @{ Name = "deploymentSpId"; Value = '${{ env.DEPLOYMENT_SP_ID }}' } - ) + ) | ForEach-Object { [PSCustomObject]$PSItem } - $DefaultParameterFileTokens = $DefaultParameterFileTokens | ForEach-Object { [PSCustomObject]$PSItem } + # Get additional Custom Parameter File Tokens from input + Write-Verbose 'Additional Custom Parameter File Tokens: ${{ inputs.customParameterFileTokens }}' -Verbose + $OtherCustomParameterFileTokens = '${{ inputs.customParameterFileTokens }}' | ConvertFrom-Json # Construct Token Function Input $ConvertTokensInputs = @{ ParameterFilePath = '${{ inputs.parameterFilePath }}' DefaultParameterFileTokens = $DefaultParameterFileTokens + OtherCustomParameterFileTokens = $OtherCustomParameterFileTokens LocalCustomParameterFileTokens = $Settings.parameterFileTokens.localTokens.tokens TokenPrefix = $Settings.parameterFileTokens.tokenPrefix TokenSuffix = $Settings.parameterFileTokens.tokenSuffix diff --git a/.github/workflows/platform.dependencies.yml b/.github/workflows/platform.dependencies.yml index 3a7c306611..83c4d0778f 100644 --- a/.github/workflows/platform.dependencies.yml +++ b/.github/workflows/platform.dependencies.yml @@ -62,6 +62,8 @@ jobs: namespace: 'Microsoft.ManagedIdentity\userAssignedIdentities' needs: - job_deploy_rg + outputs: + msiPrincipalId: ${{ steps.print_msi_prinId.outputs.msiPrincipalId }} strategy: fail-fast: false matrix: @@ -72,6 +74,7 @@ jobs: with: fetch-depth: 0 - name: 'Deploy module' + id: deploy_msi uses: ./.github/actions/templates/validateModuleDeployment with: templateFilePath: 'arm/${{ env.namespace }}/deploy.bicep' @@ -81,6 +84,15 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ env.removeDeployment }}' + - name: Set msi principal ID output + id: print_msi_prinId + uses: azure/powershell@v1 + with: + inlineScript: | + $deploymentOutput = '${{ steps.deploy_msi.outputs.deploymentOutput }}' + $msiPrincipalId = (ConvertFrom-Json $deploymentOutput).msiPrincipalId + Write-Output ('::set-output name={0}::{1}' -f 'msiPrincipalId', $msiPrincipalId) + azPSVersion: 'latest' job_deploy_pa: runs-on: ubuntu-20.04 @@ -573,6 +585,7 @@ jobs: - job_deploy_sa - job_deploy_evh - job_deploy_law + - job_deploy_msi strategy: fail-fast: false matrix: @@ -592,6 +605,7 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ env.removeDeployment }}' + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"${{ needs.job_deploy_msi.outputs.msiPrincipalId }}"}]' job_deploy_kv: runs-on: ubuntu-20.04 @@ -602,6 +616,7 @@ jobs: - job_deploy_sa - job_deploy_evh - job_deploy_law + - job_deploy_msi strategy: fail-fast: false matrix: @@ -621,6 +636,7 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ env.removeDeployment }}' + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"${{ needs.job_deploy_msi.outputs.msiPrincipalId }}"}]' job_deploy_kv_secrets: runs-on: ubuntu-20.04 @@ -714,6 +730,7 @@ jobs: - job_deploy_sa - job_deploy_evh - job_deploy_law + - job_deploy_msi strategy: fail-fast: false matrix: @@ -733,6 +750,7 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ env.removeDeployment }}' + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"${{ needs.job_deploy_msi.outputs.msiPrincipalId }}"}]' job_deploy_sqlmi_kv_secrets: runs-on: ubuntu-20.04 @@ -853,6 +871,7 @@ jobs: subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' removeDeployment: '${{ env.removeDeployment }}' + customParameterFileTokens: '[{"Name":"msiPrincipalId","Value":"${{ needs.job_deploy_msi.outputs.msiPrincipalId }}"}]' job_deploy_vnet: runs-on: ubuntu-20.04 diff --git a/docs/wiki/TestingDesign.md b/docs/wiki/TestingDesign.md index 4dd6a84ef1..16d255850e 100644 --- a/docs/wiki/TestingDesign.md +++ b/docs/wiki/TestingDesign.md @@ -113,8 +113,8 @@ Since also dependency resources are in turn subject to dependencies with each ot **Second level resources**: This group of resources has a dependency only on the resource group which will host them. Resources in this group can be deployed in parallel. - 1. User assigned identity: This resource is leveraged by all dependency resources - > **Note**: The object ID of the [user assigned identity] must be set in several dependency parameter files. However, when you first run the pipeline, this object ID will be unknown. It is hence recommended to either manually create the MSI beforehand - or - run the pipeline without the ID once (which will cause the pipeline to fail during the ID's usage, but **after** the MSI was deployed), then update the value in the parameter files and finally re-run the pipeline. + 1. User assigned identity: This resource is leveraged by the [role assignment], [key vault] and [recovery services vault] dependency resources. + > **Note**: The object ID of the [user assigned identity] is needed by several dependency parameter files. However, before running the dependency pipeline for the first time, the [user assigned identity] resource does not exist yet, thus its object ID is unknown. For this reason, instead of the object ID value, some dependency parameter files contain the `"<>"` token, for which the correct value is retrieved and replaced by the pipeline at runtime. 1. Policy assignment: This resource is leveraged by the [policy exemption] resource. 1. Log analytics workspace: This resource is leveraged by all resources supporting diagnostic settings on LAW. 1. Storage account: This resource is leveraged by all resources supporting diagnostic settings on a storage account. diff --git a/utilities/pipelines/dependencies/Microsoft.Authorization/roleAssignments/parameters/parameters.json b/utilities/pipelines/dependencies/Microsoft.Authorization/roleAssignments/parameters/parameters.json index 4a63cc7d1e..d7f912954c 100644 --- a/utilities/pipelines/dependencies/Microsoft.Authorization/roleAssignments/parameters/parameters.json +++ b/utilities/pipelines/dependencies/Microsoft.Authorization/roleAssignments/parameters/parameters.json @@ -6,7 +6,7 @@ "value": "Contributor" }, "principalId": { - "value": "cf33fea8-b30f-424f-ab73-c48d99e0b222" // The object ID of the deployed MSI + "value": "<>" // The object ID of the deployed MSI. Replaced by the pipeline }, "subscriptionId": { "value": "<>" diff --git a/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/parameters.json b/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/parameters.json index 6d32bb2116..626ac4d03a 100644 --- a/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/parameters.json +++ b/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/parameters.json @@ -26,7 +26,7 @@ }, { "tenantId": "<>", - "objectId": "cf33fea8-b30f-424f-ab73-c48d99e0b222", // adding adp-sxx-az-msi-x-001 to get secrets + "objectId": "<>", // The object ID of the deployed MSI. Replaced by the pipeline "permissions": { "keys": [], "secrets": [ diff --git a/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/pe.parameters.json b/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/pe.parameters.json index 86d8b66830..e921e73da9 100644 --- a/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/pe.parameters.json +++ b/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/pe.parameters.json @@ -9,7 +9,7 @@ "value": [ { "tenantId": "<>", - "objectId": "cf33fea8-b30f-424f-ab73-c48d99e0b222", // adding adp-sxx-az-msi-x-001 to get secrets + "objectId": "<>", // The object ID of the deployed MSI. Replaced by the pipeline "permissions": { "keys": [], "secrets": [ diff --git a/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/sqlmi.parameters.json b/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/sqlmi.parameters.json index 8c586acbcf..ead3ca98aa 100644 --- a/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/sqlmi.parameters.json +++ b/utilities/pipelines/dependencies/Microsoft.KeyVault/vaults/parameters/sqlmi.parameters.json @@ -26,7 +26,7 @@ }, { "tenantId": "<>", - "objectId": "cf33fea8-b30f-424f-ab73-c48d99e0b222", // adding adp-sxx-az-msi-x-001 to get secrets + "objectId": "<>", // The object ID of the deployed MSI. Replaced by the pipeline "permissions": { "keys": [ "Get", diff --git a/utilities/pipelines/dependencies/Microsoft.RecoveryServices/vaults/parameters/parameters.json b/utilities/pipelines/dependencies/Microsoft.RecoveryServices/vaults/parameters/parameters.json index 7a84003ad2..258acb9495 100644 --- a/utilities/pipelines/dependencies/Microsoft.RecoveryServices/vaults/parameters/parameters.json +++ b/utilities/pipelines/dependencies/Microsoft.RecoveryServices/vaults/parameters/parameters.json @@ -254,7 +254,7 @@ { "roleDefinitionIdOrName": "Reader", "principalIds": [ - "cf33fea8-b30f-424f-ab73-c48d99e0b222" // The object ID of the deployed MSI + "<>" // The object ID of the deployed MSI. Replaced by the pipeline ] } ]