Description
This issue is intended to track the efforts for alinging the CARML modules to the Azure Verified Module (AVM) specs, ultimately enabling us to publish the CARML modules to the Public Bicep Registry.
Bulk edits
These can ideally be updated 'on scale', with the already defined interfaces & conventions (e.g., folder structure), that must be updated in all modules (while taking individual characterists into account).
Note: Some of these changes may require changes to the CI environment.
Checklist
Per-module edits
Side-by-side / following the alignment to the extension interface, the following list should be used to track the 'full' alignment of the modules (e.g., PSRule compliance, etc.)
Checklist
Migration Guide
This section provides a checklist of things to look out for per module to ensure they're AVM compliant, both as per module specifications & the Contribution Guide.
Checklist
-
Tests (ref)
-
For each module that supports
Diagnostic Settings
Reference to AVM specs
NOTE: ⚠️ Make sure that if the module does not support e.g. metrics, that you update the logic accordingly
Role Assignments
Reference to AVM specs
Resource Locks
Reference to AVM specs
Tags
Reference to AVM specs
Managed Identities
Reference to AVM specs
NOTE: ⚠️ Make sure that if the module does not support e.g. user-assigned-identities, that you update the logic accordingly
Private Endpoints
Reference to AVM specs
Customer Managed Keys
Reference to AVM specs
NOTE: ⚠️ Make sure that if the module does not support e.g. metrics, that you update the logic accordingly
-
Other
-
ReadMe
Helper script (work in progress)
Snippet
#region helper functions
function Convert-Folders {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $FolderPath
)
# .bicep
(Get-ChildItem -Path $FolderPath -Directory -Recurse -Filter '.bicep').FullName | Where-Object { $_ } | ForEach-Object {
if (-not (Test-Path (Join-Path (Split-Path $_) 'modules'))) {
$null = Rename-Item -Path $_ -NewName 'modules' -Force
}
}
# .test
# Add [e2e] folder
(Get-ChildItem -Path $FolderPath -Directory -Recurse -Filter '.test').FullName | ForEach-Object {
$newPath = Join-Path $_ 'e2e'
if (-not (Test-Path $newPath)) {
$null = New-Item -ItemType 'Directory' -Path $newPath
}
}
# Move tests to new sub folder
$testTopFolders = (Get-ChildItem -Path $FolderPath -Directory -Recurse -Filter '.test').FullName
foreach ($testfolderPath in $testTopFolders) {
$testCaseFolderPaths = (Get-ChildItem $testfolderPath -Directory -Exclude 'e2e').FullName
foreach ($testFolderPath in $testCaseFolderPaths) {
$expectedFolderName = Split-Path $testfolderPath -Leaf
$newTestFolderPath = Join-Path (Split-Path $testFolderPath -Parent) 'e2e' $expectedFolderName
if (-not (Test-Path $newTestFolderPath)) {
$null = New-Item -ItemType 'Directory' -Path $newTestFolderPath
}
$originalTestFilePaths = Get-ChildItem $testfolderPath -File -Recurse
foreach ($originalTestFilePath in $originalTestFilePaths) {
$null = Move-Item -Path $originalTestFilePath -Destination $newTestFolderPath -Force
}
# If default folder [min/common], rename to new names [defaults/max]
switch ($expectedFolderName) {
'min' {
if (-not (Test-Path (Join-Path (Split-Path $newTestFolderPath) 'defaults'))) {
$null = Rename-Item -Path $newTestFolderPath -NewName 'defaults' -Force
}
}
'common' {
if (-not (Test-Path (Join-Path (Split-Path $newTestFolderPath) 'max'))) {
$null = Rename-Item -Path $newTestFolderPath -NewName 'max' -Force
}
}
Default {}
}
# Delete original folder
$null = Remove-Item -Path $testFolderPath -Force
}
}
# Rename test folder
(Get-ChildItem -Path $FolderPath -Directory -Recurse -Filter '.test').FullName | Rename-Item -NewName 'tests'
# Generate WAF folder
$wafFolderPath = Join-Path $FolderPath 'tests' 'e2e' 'waf-aligned'
if (-not (Test-Path $wafFolderPath)) {
# Duplicate 'max' test folder
$null = New-Item -Path $wafFolderPath -ItemType 'Directory' -Force
$maxTestPath = Join-Path $FolderPath 'tests' 'e2e' 'max'
$null = Copy-Item -Path "$maxTestPath/*" -Destination $wafFolderPath -Recurse
}
}
function Set-ReadMe {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $FolderPath
)
$readMePaths = (Get-ChildItem -Path $FolderPath -File -Recurse -Filter 'readme.md').FullName
# Remove original ReadMes
$readMePaths | ForEach-Object { $null = Remove-Item -Path $_ -Force }
# Regenerate new ReadMes
# Set-AVMModule -ModuleFolderPath $FolderPath -Recurse
}
function Set-VersionFile {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $FolderPath
)
$versionPaths = (Get-ChildItem -Path $FolderPath -File -Recurse -Filter 'version.json').FullName
foreach ($path in $versionPaths) {
$originalContent = Get-Content -Path $path -Raw | ConvertFrom-Json
$originalContent.version = '0.1'
$originalContent | ConvertTo-Json -Depth 100 | Set-Content -Path $path -Force
}
}
function Set-TestFiles {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $FolderPath
)
$testFilePaths = (Get-ChildItem -Path $FolderPath -File -Recurse -Filter 'main.test.bicep').FullName
$testFolderPaths = $testFilePaths | ForEach-Object { Split-Path $_ }
# [1] Update service shorts
foreach ($testFolderPath in $testFolderPaths) {
$testFolderName = Split-Path $testFolderPath -Leaf
# If default folder [min/common], rename to new names [defaults/max]
switch ($testFolderName) {
'defaults' {
$testContent = Get-Content (Join-Path $testFolderPath 'main.test.bicep') -Raw
# Should remain `min` for now to work with PSRule config
# $testContent = $testContent -replace "(param serviceShort string = '[a-zA-Z]+)min'", ("`$1{0}'" -f 'min')
$null = Set-Content (Join-Path $testFolderPath 'main.test.bicep') -Value $testContent -Force
}
'max' {
$testContent = Get-Content (Join-Path $testFolderPath 'main.test.bicep') -Raw
$testContent = $testContent -replace "(param serviceShort string = '[a-zA-Z]+)com'", ("`$1{0}'" -f 'max')
$null = Set-Content (Join-Path $testFolderPath 'main.test.bicep') -Value $testContent -Force
}
'waf-aligned' {
$testContent = Get-Content (Join-Path $testFolderPath 'main.test.bicep') -Raw
$testContent = $testContent -replace "(param serviceShort string = '[a-zA-Z]+)com'", ("`$1{0}'" -f 'waf')
$null = Set-Content (Join-Path $testFolderPath 'main.test.bicep') -Value $testContent -Force
}
Default {}
}
}
foreach ($testFilePath in $testFilePaths) {
$testContent = Get-Content $testFilePath -Raw
# [2] Remove telemetry
$testContent = $testContent -replace "@description\('.+\(GUID\)\.'\)(\r\n|\r|\n){1}param enableDefaultTelemetry.+(\r\n|\r|\n){2}", ''
$testContent = $testContent -replace '.*enableDefaultTelemetry: enableDefaultTelemetry.*(\r\n|\r|\n){1}', ''
# [3] Update RG name
$testContent = $testContent -replace "(param resourceGroupName string = ')ms.(.+)", '$1dep-${namePrefix}-$2'
# [4] Idempotency & main.bicep reference
$testContent = $testContent -replace "module testDeployment '\.\.\/\.\.\/main\.bicep' = {", "@batchSize(1)`r`nmodule testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: {"
$testContent = $testContent -replace "name: '\`${uniqueString\(deployment\(\)\.name, location\)}-test-\`${serviceShort}'", "name: '`${uniqueString(deployment().name, location)}-test-`${serviceShort}-`${iteration}'"
# [5] diagnosticsTemplateReference
$testContent = $testContent -replace '.+diagnostic.dependencies.bicep', "module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep"
# [6] Replace namePrefix token with new format
$testContent = $testContent -replace '\[\[namePrefix\]\]', '#_namePrefix_#'
$null = Set-Content -Path $testFilePath -Value $testContent -Force
}
}
#endregion
function Convert-CarmlToAvm {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $FolderPath,
[Parameter(Mandatory = $false)]
[string] $RepoRoot = 'C:\dev\ip\Azure-ResourceModules\ResourceModules'
)
# Load external functions
# . (Join-Path $RepoRoot 'utilities' 'tools' 'Set-AVMModule.ps1')
# [1] Convert folders
Convert-Folders -FolderPath $FolderPath
# [2] Set version.json back to 0.1
Set-VersionFile -FolderPath $FolderPath
# [3] Test file changes
Set-TestFiles -FolderPath $FolderPath
# [4] Regenerate ReadMe
Set-ReadMe -FolderPath $FolderPath
}
Convert-CarmlToAvm -FolderPath 'C:\dev\ip\Azure-ResourceModules\ResourceModules\modules\key-vault\vault'
Final steps: Migration
For the final AVM contribution a few more changes will be necessary as described in the following
These changes should be done after creating a fork of the public bicep registory respository and the module is added in the /avm/res/ folder. From there you can not only test the module using the AVM CI, but also open the Pull Request to the Upstream repository.
Description
This issue is intended to track the efforts for alinging the CARML modules to the Azure Verified Module (AVM) specs, ultimately enabling us to publish the CARML modules to the Public Bicep Registry.
Bulk edits
These can ideally be updated 'on scale', with the already defined interfaces & conventions (e.g., folder structure), that must be updated in all modules (while taking individual characterists into account).
Checklist
Set-ModuleReadMescript is updated, remove & regenerate all ReadMesnullvalues) #4207Per-module edits
Side-by-side / following the alignment to the extension interface, the following list should be used to track the 'full' alignment of the modules (e.g., PSRule compliance, etc.)
Checklist
aad/domain-serviceto AVM specsanalysis-services/serverto AVM specs #4398api-management/serviceto AVM specs #4355app/container-appto AVM specs #4489app/jobsto AVM specsapp/managed-environmentto AVM specs #4442app-configuration/configuration-storeto AVM specs #4504automation/automation-accountto AVM specs #4311batch/batch-accountto AVM specs #4056cache/redisto AVM specs #4399cache/redis-enterpriseto AVM specscdn/profileto AVM specs #4510cognitive-services/accountto AVM specs #4055compute/availability-setto AVM specs #4411compute/diskto AVM specs #4406compute/disk-encryption-setto AVM specs #4425compute/galleryto AVM specs #4422compute/imageto AVM specs #4400compute/proximity-placement-groupto AVM specs #4426compute/ssh-public-keyto AVM specs #4075compute/virtual-machineto AVM specs #4107compute/virtual-machine-scale-setto AVM specs #4502consumption/budgetto AVM specs #4407container-instance/container-groupto AVM specs #4481container-registry/registryto AVM specs #4456container-service/managed-clusterto AVM specs #4194data-factory/factoryto AVM specs #4376data-protection/backup-vaultto AVM specs #4405databricks/workspaceto AVM specs #4402db-for-my-sql/flexible-serverto AVM specs #4401db-for-postgre-sql/flexible-serverto AVM specs #4305desktop-virtualization/application-groupto AVM specs #4473desktop-virtualization/host-poolto AVM specs #4475desktop-virtualization/scaling-planto AVM specs #4474desktop-virtualization/workspaceto AVM specs #4476dev-test-lab/labto AVM specs #4412digital-twins/digital-twins-instanceto AVM specs #4141document-db/database-accountto AVM specs #4321event-grid/domainto AVM specs #4384event-grid/system-topicto AVM specs #4148event-grid/topicto AVM specs #4385event-hub/namespaceto AVM specs #4479health-bot/health-botto AVM specs #4404healthcare-apis/workspaceto AVM specs #4531insights-actiongroupto AVM specs #4074insights/activity-log-alertto AVM specs #4344insights/componentto AVM specs #4268insights/data-collection-endpointto AVM specs #4332insights/data-collection-ruleto AVM specs #4333insights/diagnostic-settingto AVM specs #4249insights/metric-alertto AVM specs #4343insights/private-link-scopeto AVM specs #4413insights/scheduled-query-ruleto AVM specs #4345insights/webtestto AVM specs #4377key-vault/vaultto AVM specs #4063kubernetes-configuration/extensionto AVM specs #4054kubernetes-configuration/flux-configurationto AVM specs #4053logic/workflowto AVM specs #4180machine-learning-services/workspaceto AVM specs #4458maintenance/maintenance-configurationto AVM specs #4378managed-identity/user-assigned-identityto AVM specs #4149managed-services/registration-definitionto AVM specsmanagement/management-groupto AVM specs #4496net-app/net-app-accountto AVM specs #4403network/application-gatewayto AVM specsnetwork/application-gateway-web-application-firewall-policyto AVM specs #4532network/application-security-groupto AVM specs #4490network/azure-firewallto AVM specs #4507network/bastion-hostto AVM specs #4324network/connectionto AVM specs #4389network/ddos-protection-planto AVM specs #4408network/dns-forwarding-rulesetto AVM specs #4139network/dns-resolverto AVM specs #4101network/dns-zoneto AVM specs #4163network/express-route-circuitto AVM specs #4264network/express-route-gatewayto AVM specs #4265network/firewall-policyto AVM specs #4432network/front-doorto AVM specs #4433network/front-door-web-application-firewall-policyto AVM specs #4434network/ip-groupto AVM specs #4414network/load-balancerto AVM specs #4044network/local-network-gatewayto AVM specs #4383network/nat-gatewayto AVM specs #4382network/network-interfaceto AVM specs #4062network/network-managerto AVM specs #4415network/network-security-groupto AVM specs #4443network/private-dns-zoneto AVM specs #4140network/private-endpointto AVM specs #4064network/private-link-serviceto AVM specs #4416network/public-ip-addressto AVM specs #4043network/public-ip-prefixto AVM specs #4323network/route-tableto AVM specs #4444network/service-endpoint-policyto AVM specsnetwork/trafficmanagerprofileto AVM specs #4314network/virtual-hubto AVM specsnetwork/virtual-networkto AVM specs #4061network/virtual-network-gatewayto AVM specs #4386network/virtual-wanto AVM specsnetwork/vpn-gatewayto AVM specs #4387network/vpn-siteto AVM specs #4390operational-insights/workspaceto AVM specs #4060operations-management/solutionto AVM specs #4059power-bi-dedicated/capacityto AVM specs #4337purview/accountto AVM specs #4460recovery-services/vaultto AVM specs #4494relay/namespaceto AVM specs #4528resource-graph/queryto AVM specs #4445resources/deployment-scriptto AVM specs #4198resources/resource-groupto AVM specs #4430search/search-serviceto AVM specs #4266security/azure-security-centerto AVM specsservice-bus/namespaceto AVM specs #4179service-fabric/clusterto AVM specssignal-r-service/signal-rto AVM specs #4511signal-r-service/web-pub-subto AVM specs #4514sql/managed-instanceto AVM specssql/serverto AVM specs #4270storage/storage-accountto AVM specs #4058synapse/private-link-hubto AVM specs #4480synapse/workspaceto AVM specs #4467virtual-machine-images/image-templateto AVM specs #4417web/connectionto AVM specs #4529web/hosting-environmentto AVM specsweb/serverfarmto AVM specs #4423web/siteto AVM specs #4438web/static-siteto AVM specs #4446Migration Guide
This section provides a checklist of things to look out for per module to ensure they're AVM compliant, both as per module specifications & the Contribution Guide.
Checklist
Tests (ref)
e2efolder (ref)minfolder todefaultscommonfolder tomaxwaf-alignedfolder (e.g., based oncommon). This test should not fail PSRule & show the module being deployed with best-practicesserviceShortparameter to align with the new naming (e.g.,wafforwaf-aligned). For now, we should continue usingminfordefaultsto align with PSRule.namePrefixinput parameter value from[[namePrefix]]to#_namePrefix_#(the reason being that Bicep has a compilation issue because of the prefix & suffix in another location)../../main.bicepmodule template reference to../../../main.bicepnamePrefix. For example:For each module that supports
Diagnostic Settings
diagnosticSettingTypedescribed in the above reference to a// Definitionsblock at the bottom of the template filediagnosticSettingsparameter as per the specs to the templateRole Assignments
roleAssignmentTypedescribed in the above reference to a// Definitionsblock at the bottom of the template fileroleAssignmentsparameter as per the specs (- should now reference the User-defined-type)builtInRoleNamesfrom thenested_roleAssignments.bicepfile and add them to the variables block of the main template. The new schema does not require the nested template. Also, reduce the list of specified roles to only those that make sense for this resource (ref)/ For, for example, Cognitive Services, we should only provide the important ones as Owner, Contributor, etc. + all service specific roles such as 'Cognitive Services User'.Resource Locks
lockTypedescribed in the above reference to a// Definitionsblock at the bottom of the template filelockparameter as per the specs (- should now reference the User-defined-type)Tags
tagsparameter as per the specsManaged Identities
managedIdentitiesTypedescribed in the above reference to a// Definitionsblock at the bottom of the template filemanagedIdentitiesparameter as per the specs to the templatePrivate Endpoints
privateEndpointTypedescribed in the above reference to a// Definitionsblock at the bottom of the template fileprivateEndpointsparameter as per the specs (- should now reference the User-defined-type)Customer Managed Keys
customerManagedKeyTypedescribed in the above reference to a// Definitionsblock at the bottom of the template filecustomerManagedKeyparameter as per the specs to the templateexistingresource references as per the specs-
- Note also that the new schema SHOULD support system-assigned-identities. As this cannot be done in a single deployment, you can find a reference how this would look like here
Other
version.jsonback to0.1nullablefeature for parameters where-ever it makes sense to you (and ensure to test it). This enables us to simplify logic like in the following exampleReadMe
main.bicepfileHelper script (work in progress)
Snippet
Final steps: Migration
For the final AVM contribution a few more changes will be necessary as described in the following
avm/utilities/e2e-template-assets/templates/diagnostic.dependencies.bicepenableTelemetryflag through likeenableTelemetry: enableTelemetryto enable users to enable/disable for the entire deployment. Child resources should remain with the telemetry switched off for now.version.jsonfiles from child modules (as can't publish them yet)ORPHANED.mdfile with the following content to the module in AVM onlyThese changes should be done after creating a fork of the public bicep registory respository and the module is added in the
/avm/res/folder. From there you can not only test the module using the AVM CI, but also open the Pull Request to the Upstream repository.