Skip to content

Commit

Permalink
feat: Enable tenant/constributor-specific parameters in test cases (A…
Browse files Browse the repository at this point in the history
…zure#3015)

## Description

- Example implementation to dynamically inject
tenant/contributor-specific information into test cases
- Tests
- Using GitHub Secrets: Failed as not dynamic. All secrets would need to
be declared explicitely in the templates, requiring continuous updates
- Using Key Vault: Works, but requires an Azure Key Vault in the test
subscription + granting the deploying principal Key Vault Secret User
permissions on set Key vault
- Secret values are fetched by prefix (currently `CI-`) and only if a
given test template has a parameter of the same name.
- Secrets are passed as secureStrings from beginning to end (i.e., also
the parameter **must** be declared as `@secure()`
- The test template parameter must follow the same naming as the secret
to be matched as per the following rules
  -  `CI-MySecret` would be matched wih a parameter named `mySecret`

Remaining TODOs

- [x] Agree on naming of variable & prefix
- [x] Test whether the solution is robust enough to not fail if the Key
Vault name variable is not set ([Test Run - See attempt
Azure#1](https://github.com/Azure/bicep-registry-modules/actions/runs/10559737937))
  <details><summary>Result</summary>

The lack of the variable does **not** fail the pipeline as shown in the
following run. The logic goes past the point where it checks for the Key
Vault

![image](https://github.com/user-attachments/assets/8756cf62-bfef-473e-929f-01611ba560ee)

  </details>

@clintgrove, @rahalan & @segraef, please note that if this PR is merged
it means you would need to update your forks in order to continue
contributing to your modules. As the names etc. can still change I'd
advice against any action at this point, but you should be the first to
know :) On the plus side, no more updating of tenant-specific values 😉

### GitHub Settings 

![image](https://github.com/user-attachments/assets/2af9a297-6938-4a29-b926-16e4148d949e)

### Azure KeyVault

![image](https://github.com/user-attachments/assets/24e57585-571e-4494-9aa7-de64feeb2d15)


### Pipeline logic in action

![image](https://github.com/user-attachments/assets/4efa1919-e09d-46af-ac4d-52cbfbbeef3e)


## Pipeline Reference

<!-- Insert your Pipeline Status Badge below -->

| Pipeline |
| -------- |

[![avm.res.managed-services.registration-definition](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml/badge.svg?branch=users%2Falsehr%2FsecretPassThru&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml)

[![avm.res.compute.virtual-machine](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml/badge.svg?branch=users%2Falsehr%2FsecretPassThru&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml)

[![avm.res.databricks.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=users%2Falsehr%2FsecretPassThru&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml)
## Type of Change

<!-- Use the checkboxes [x] on the options that are relevant. -->

- [ ] Update to CI Environment or utilities (Non-module affecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
  - [ ] Update to documentation

## Checklist

- [ ] I'm sure there are no other open Pull Requests for the same
update/change
- [ ] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [ ] My corresponding pipelines / checks run clean and green without
any errors or warnings

<!-- Please keep up to date with the contribution guide at
https://aka.ms/avm/contribute/bicep -->

---------

Co-authored-by: Jack Tracey <[email protected]>
  • Loading branch information
AlexanderSehr and jtracey93 authored Sep 1, 2024
1 parent 604beb8 commit d1a5a44
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 17 deletions.
37 changes: 37 additions & 0 deletions .github/actions/templates/avm-validateModuleDeployment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ runs:
AdditionalParameters = @{}
}
# Add custom parameters as needed
if($moduleTemplatePossibleParameters -contains 'resourceLocation') {
$functionInput.AdditionalParameters += @{
resourceLocation = '${{ steps.get-resource-location.outputs.resourceLocation }}'
Expand All @@ -219,6 +220,24 @@ runs:
}
}
# Fetch & add custom secrets, if any
# -----------------------------------
$keyVaultName = "${{ env.CI_KEY_VAULT_NAME }}"
if(-not [String]::IsNullOrEmpty($keyVaultName)) {
# Note: This action requires at least 'Key Vault Secrets User' permissions
$customKeyVaultSecrets = Get-AzKeyVaultSecret -VaultName $keyVaultName | Where-Object { $_.Name -match '^CI-.+' }
foreach($customSecret in $customKeyVaultSecrets) {
$formattedName = $customSecret.Name -replace '^CI-' # e.g. 'CI-mySecret' -> 'mySecret'
if($moduleTemplatePossibleParameters -contains $formattedName) {
Write-Verbose ('Setting value for parameter [{0}]' -f $formattedName) -Verbose
$functionInput.AdditionalParameters += @{
$formattedName = (Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $customSecret.Name).SecretValue
}
}
}
}
Write-Verbose 'Invoke task with' -Verbose
Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose
Expand Down Expand Up @@ -280,6 +299,24 @@ runs:
}
}
# Fetch & add custom secrets, if any
# -----------------------------------
$keyVaultName = "${{ env.CI_KEY_VAULT_NAME }}"
if(-not [String]::IsNullOrEmpty($keyVaultName)) {
# Note: This action requires at least 'Key Vault Secrets User' permissions
$customKeyVaultSecrets = Get-AzKeyVaultSecret -VaultName $keyVaultName | Where-Object { $_.Name -match '^CI-.+' }
foreach($customSecret in $customKeyVaultSecrets) {
$formattedName = $customSecret.Name -replace '^CI-' # e.g. 'CI-mySecret' -> 'mySecret'
if($moduleTemplatePossibleParameters -contains $formattedName) {
Write-Verbose ('Setting value for parameter [{0}]' -f $formattedName) -Verbose
$functionInput.AdditionalParameters += @{
$formattedName = (Get-AzKeyVaultSecret -VaultName $keyVaultName -Name $customSecret.Name).SecretValue
}
}
}
}
Write-Verbose 'Invoke task with' -Verbose
Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/avm.template.module.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ env:
ARM_MGMTGROUP_ID: "${{ secrets.ARM_MGMTGROUP_ID }}"
ARM_TENANT_ID: "${{ secrets.ARM_TENANT_ID }}"
TOKEN_NAMEPREFIX: "${{ secrets.TOKEN_NAMEPREFIX }}"
CI_KEY_VAULT_NAME: "${{ vars.CI_KEY_VAULT_NAME }}"

jobs:
#########################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ param serviceShort string = 'cvmlinmax'
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The object id of the Backup Management Service Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-BackupManagementServiceEnterpriseApplicationObjectId\'.')
@secure()
param backupManagementServiceEnterpriseApplicationObjectId string

// ============ //
// Dependencies //
// ============ //
Expand Down Expand Up @@ -47,7 +51,7 @@ module nestedDependencies 'dependencies.bicep' = {
sshDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}'
sshKeyName: 'dep-${namePrefix}-ssh-${serviceShort}'
dcrName: 'dep-${namePrefix}-dcr-${serviceShort}'
backupManagementServiceApplicationObjectId: 'be766fc3-eac4-4627-b8f5-298e35c8aea4' // Tenant-specific Backup Management Service Enterprise Application Object Id
backupManagementServiceApplicationObjectId: backupManagementServiceEnterpriseApplicationObjectId
logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ param password string = newGuid()
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The object id of the Backup Management Service Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-BackupManagementServiceEnterpriseApplicationObjectId\'.')
@secure()
param backupManagementServiceEnterpriseApplicationObjectId string

// ============ //
// Dependencies //
// ============ //
Expand All @@ -50,7 +54,7 @@ module nestedDependencies 'dependencies.bicep' = {
storageAccountName: 'dep${namePrefix}sa${serviceShort}01'
storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}'
proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}'
backupManagementServiceApplicationObjectId: 'be766fc3-eac4-4627-b8f5-298e35c8aea4' // Tenant-specific Backup Management Service Enterprise Application Object Id
backupManagementServiceApplicationObjectId: backupManagementServiceEnterpriseApplicationObjectId
dcrName: 'dep-${namePrefix}-dcr-${serviceShort}'
logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ param password string = newGuid()
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The object id of the Backup Management Service Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-BackupManagementServiceEnterpriseApplicationObjectId\'.')
@secure()
param backupManagementServiceEnterpriseApplicationObjectId string

// ============ //
// Dependencies //
// ============ //
Expand All @@ -49,7 +53,7 @@ module nestedDependencies 'dependencies.bicep' = {
storageAccountName: 'dep${namePrefix}sa${serviceShort}01'
storageUploadDeploymentScriptName: 'dep-${namePrefix}-sads-${serviceShort}'
proximityPlacementGroupName: 'dep-${namePrefix}-ppg-${serviceShort}'
backupManagementServiceApplicationObjectId: 'be766fc3-eac4-4627-b8f5-298e35c8aea4' // Tenant-specific Backup Management Service Enterprise Application Object Id
backupManagementServiceApplicationObjectId: backupManagementServiceEnterpriseApplicationObjectId
dcrName: 'dep-${namePrefix}-dcr-${serviceShort}'
logAnalyticsWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId
}
Expand Down
6 changes: 5 additions & 1 deletion avm/res/databricks/workspace/tests/e2e/max/main.test.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ param baseTime string = utcNow('u')
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The object id of the AzureDatabricks Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDatabricksEnterpriseApplicationObjectId\'.')
@secure()
param azureDatabricksEnterpriseApplicationObjectId string

// ============ //
// Dependencies //
// ============ //
Expand All @@ -47,7 +51,7 @@ module nestedDependencies 'dependencies.bicep' = {
storageAccountName: 'dep${namePrefix}sa${serviceShort}'
virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}'
databricksApplicationObjectId: '711330f9-cfad-4b10-a462-d82faa92027d' // Tenant-specific 'AzureDatabricks' Enterprise Application Object Id
databricksApplicationObjectId: azureDatabricksEnterpriseApplicationObjectId
keyVaultDiskName: 'dep-${namePrefix}-kve-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
// Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ param baseTime string = utcNow('u')
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The object id of the AzureDatabricks Enterprise Application. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDatabricksEnterpriseApplicationObjectId\'.')
@secure()
param azureDatabricksEnterpriseApplicationObjectId string

// ============ //
// Dependencies //
// ============ //
Expand All @@ -47,7 +51,7 @@ module nestedDependencies 'dependencies.bicep' = {
storageAccountName: 'dep${namePrefix}sa${serviceShort}'
virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}'
networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}'
databricksApplicationObjectId: '711330f9-cfad-4b10-a462-d82faa92027d' // Tenant-specific 'AzureDatabricks' Enterprise Application Object Id
databricksApplicationObjectId: azureDatabricksEnterpriseApplicationObjectId
keyVaultDiskName: 'dep-${namePrefix}-kve-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
// Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total)
keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}'
Expand Down
16 changes: 8 additions & 8 deletions avm/res/managed-services/registration-definition/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: '<managedByTenantId>'
name: 'Component Validation - msrdmin Subscription assignment'
registrationDescription: 'Managed by Lighthouse'
// Non-required parameters
Expand Down Expand Up @@ -97,7 +97,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
]
},
"managedByTenantId": {
"value": "449fbe1d-9c99-4509-9014-4fd5cf25b014"
"value": "<managedByTenantId>"
},
"name": {
"value": "Component Validation - msrdmin Subscription assignment"
Expand Down Expand Up @@ -149,7 +149,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
roleDefinitionId: '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: '<managedByTenantId>'
name: 'Component Validation - msrdmax Subscription assignment'
registrationDescription: 'Managed by Lighthouse'
// Non-required parameters
Expand Down Expand Up @@ -194,7 +194,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
]
},
"managedByTenantId": {
"value": "449fbe1d-9c99-4509-9014-4fd5cf25b014"
"value": "<managedByTenantId>"
},
"name": {
"value": "Component Validation - msrdmax Subscription assignment"
Expand Down Expand Up @@ -240,7 +240,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: '<managedByTenantId>'
name: 'Component Validation - msrdrg Subscription assignment'
registrationDescription: 'Managed by Lighthouse'
// Non-required parameters
Expand Down Expand Up @@ -277,7 +277,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
]
},
"managedByTenantId": {
"value": "449fbe1d-9c99-4509-9014-4fd5cf25b014"
"value": "<managedByTenantId>"
},
"name": {
"value": "Component Validation - msrdrg Subscription assignment"
Expand Down Expand Up @@ -326,7 +326,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: '<managedByTenantId>'
name: 'Component Validation - msrdwaf Subscription assignment'
registrationDescription: 'Managed by Lighthouse'
// Non-required parameters
Expand Down Expand Up @@ -362,7 +362,7 @@ module registrationDefinition 'br/public:avm/res/managed-services/registration-d
]
},
"managedByTenantId": {
"value": "449fbe1d-9c99-4509-9014-4fd5cf25b014"
"value": "<managedByTenantId>"
},
"name": {
"value": "Component Validation - msrdwaf Subscription assignment"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ param resourceLocation string = deployment().location
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.')
@secure()
param lighthouseManagedByTenantId string

// ============== //
// Test Execution //
// ============== //
Expand All @@ -38,7 +42,7 @@ module testDeployment '../../../main.bicep' = [
roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: lighthouseManagedByTenantId
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ param resourceLocation string = deployment().location
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.')
@secure()
param lighthouseManagedByTenantId string

// ============== //
// Test Execution //
// ============== //
Expand Down Expand Up @@ -48,7 +52,7 @@ module testDeployment '../../../main.bicep' = [
roleDefinitionId: '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' // User Access Administrator
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: lighthouseManagedByTenantId
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ param resourceLocation string = deployment().location
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.')
@secure()
param lighthouseManagedByTenantId string

// ============ //
// Dependencies //
// ============ //
Expand Down Expand Up @@ -61,7 +65,7 @@ module testDeployment '../../../main.bicep' = [
roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: lighthouseManagedByTenantId
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ param resourceLocation string = deployment().location
@description('Optional. A token to inject into the name of each resource.')
param namePrefix string = '#_namePrefix_#'

@description('Required. The tenant Id of the lighthouse tenant. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-LighthouseManagedByTenantId\'.')
@secure()
param lighthouseManagedByTenantId string

// ============ //
// Dependencies //
// ============ //
Expand Down Expand Up @@ -54,7 +58,7 @@ module testDeployment '../../../main.bicep' = [
roleDefinitionId: '91c1777a-f3dc-4fae-b103-61d183457e46'
}
]
managedByTenantId: '449fbe1d-9c99-4509-9014-4fd5cf25b014'
managedByTenantId: lighthouseManagedByTenantId
}
}
]

0 comments on commit d1a5a44

Please sign in to comment.