Skip to content

Commit da7e5cb

Browse files
feat: New utility module - AVM Common Types (#3397)
## Description - Implemented AVM common types module which can be used to reduce duplicated code in the code base - Reduced the tests in `main.test.bicep` for UDTs as they are not compatible with using a module reference - Added a quotation-escape to the deployment action as it turned out that it breaks if you have single quotes in your deployment output Example implementation in KeyVault module: > **Note:** The type should always be implemented by name as a `*` implementation causes all types to be added to the combiled `main.json` ![image](https://github.com/user-attachments/assets/76837be7-121b-4a76-a2f8-89c924abbcd8) ![image](https://github.com/user-attachments/assets/10507e09-a289-4b9e-b97f-c6e8241cd3ca) ## Pipeline Reference <!-- Insert your Pipeline Status Badge below --> | Pipeline | | -------- | | [![avm.ptn.types.avm-common-type](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.types.avm-common-type.yml/badge.svg?branch=users%2Falsehr%2F1396_commonTypes)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.types.avm-common-type.yml) | | [![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2F1396_commonTypes)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) [run using local ref](https://github.com/AlexanderSehr/bicep-registry-modules/actions/runs/11164377047) | ## Type of Change <!-- Use the checkboxes [x] on the options that are relevant. --> - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] 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
1 parent 6fd1411 commit da7e5cb

File tree

10 files changed

+2053
-73
lines changed

10 files changed

+2053
-73
lines changed

.github/CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,5 @@
171171
/avm/res/web/serverfarm/ @Azure/avm-res-web-serverfarm-module-owners-bicep @Azure/avm-module-reviewers-bicep
172172
/avm/res/web/site/ @Azure/avm-res-web-site-module-owners-bicep @Azure/avm-module-reviewers-bicep
173173
/avm/res/web/static-site/ @Azure/avm-res-web-staticsite-module-owners-bicep @Azure/avm-module-reviewers-bicep
174+
/avm/utl/types/avm-common-types/ @Azure/avm-utl-types-avmcommontypes-module-owners-bicep @Azure/avm-module-reviewers-bicep
174175
*avm.core.team.tests.ps1 @Azure/avm-core-team-technical-bicep

.github/ISSUE_TEMPLATE/avm_module_issue.yml

+1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ body:
206206
- "avm/res/web/serverfarm"
207207
- "avm/res/web/site"
208208
- "avm/res/web/static-site"
209+
- "avm/utl/types/avm-common-types"
209210
validations:
210211
required: true
211212
- type: input

.github/actions/templates/avm-validateModuleDeployment/action.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ runs:
327327
Write-Output ('{0}={1}' -f 'deploymentNames', ($res.deploymentNames | ConvertTo-Json -Compress)) >> $env:GITHUB_OUTPUT
328328
329329
# Populate further outputs
330-
$deploymentOutput = $res.deploymentOutput | ConvertTo-Json -Depth 99 -Compress
330+
$deploymentOutput = ($res.deploymentOutput | ConvertTo-Json -Depth 99 -Compress) -replace "'", "''" # Escaping single quotes for resilient access in subsequent steps
331331
Write-Output ('{0}={1}' -f 'deploymentOutput', $deploymentOutput) >> $env:GITHUB_OUTPUT
332332
333333
Write-Verbose "Deployment output: $deploymentOutput" -Verbose
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: "avm.utl.types.avm-common-types"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
staticValidation:
7+
type: boolean
8+
description: "Execute static validation"
9+
required: false
10+
default: false # true
11+
deploymentValidation:
12+
type: boolean
13+
description: "Execute deployment validation"
14+
required: false
15+
default: true
16+
removeDeployment:
17+
type: boolean
18+
description: "Remove deployed module"
19+
required: false
20+
default: true
21+
customLocation:
22+
type: string
23+
description: "Default location overwrite (e.g., eastus)"
24+
required: false
25+
default: 'northeurope'
26+
push:
27+
branches:
28+
- main
29+
paths:
30+
- ".github/actions/templates/avm-**"
31+
- ".github/workflows/avm.template.module.yml"
32+
- ".github/workflows/avm.utl.types.avm-common-types.yml"
33+
- "avm/utl/types/avm-common-types/**"
34+
- "avm/utilities/pipelines/**"
35+
- "!avm/utilities/pipelines/platform/**"
36+
- "!*/**/README.md"
37+
38+
env:
39+
modulePath: "avm/utl/types/avm-common-types"
40+
workflowPath: ".github/workflows/avm.utl.types.avm-common-types.yml"
41+
42+
concurrency:
43+
group: ${{ github.workflow }}
44+
45+
jobs:
46+
###########################
47+
# Initialize pipeline #
48+
###########################
49+
job_initialize_pipeline:
50+
runs-on: ubuntu-latest
51+
name: "Initialize pipeline"
52+
steps:
53+
- name: "Checkout"
54+
uses: actions/checkout@v4
55+
with:
56+
fetch-depth: 0
57+
- name: "Set input parameters to output variables"
58+
id: get-workflow-param
59+
uses: ./.github/actions/templates/avm-getWorkflowInput
60+
with:
61+
workflowPath: "${{ env.workflowPath}}"
62+
- name: "Get module test file paths"
63+
id: get-module-test-file-paths
64+
uses: ./.github/actions/templates/avm-getModuleTestFiles
65+
with:
66+
modulePath: "${{ env.modulePath }}"
67+
outputs:
68+
workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}
69+
moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }}
70+
psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }}
71+
modulePath: "${{ env.modulePath }}"
72+
73+
##############################
74+
# Call reusable workflow #
75+
##############################
76+
call-workflow-passing-data:
77+
name: "Run"
78+
permissions:
79+
id-token: write # For OIDC
80+
contents: write # For release tags
81+
needs:
82+
- job_initialize_pipeline
83+
uses: ./.github/workflows/avm.template.module.yml
84+
with:
85+
workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}"
86+
moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}"
87+
psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}"
88+
modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}"
89+
secrets: inherit

avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1

+7-72
Original file line numberDiff line numberDiff line change
@@ -667,34 +667,26 @@ Describe 'Module tests' -Tag 'Module' {
667667
$udtCases = @(
668668
@{
669669
parameterName = 'diagnosticSettings'
670-
udtName = 'diagnosticSettingType'
671670
link = "$interfaceBase/diagnostic-settings"
672671
}
673672
@{
674673
parameterName = 'roleAssignments'
675-
udtName = 'roleAssignmentType'
676-
udtExpectedUrl = "$interfaceBase/role-assignments/udt-schema"
677674
link = "$interfaceBase/role-assignments"
678675
}
679676
@{
680677
parameterName = 'lock'
681-
udtName = 'lockType'
682-
udtExpectedUrl = "$interfaceBase/resource-locks/udt-schema"
683678
link = "$interfaceBase/resource-locks"
684679
}
685680
@{
686681
parameterName = 'managedIdentities'
687-
udtName = 'managedIdentitiesType'
688682
link = "$interfaceBase/managed-identities"
689683
}
690684
@{
691685
parameterName = 'privateEndpoints'
692-
udtName = 'privateEndpointType'
693686
link = "$interfaceBase/private-endpoints"
694687
}
695688
@{
696689
parameterName = 'customerManagedKey'
697-
udtName = 'customerManagedKeyType'
698690
link = "$interfaceBase/customer-managed-keys"
699691
}
700692
)
@@ -705,86 +697,29 @@ Describe 'Module tests' -Tag 'Module' {
705697
templateFileContent = $templateFileContent
706698
templateFileContentBicep = Get-Content $templateFilePath
707699
parameterName = $udtCase.parameterName
708-
udtName = $udtCase.udtName
709700
expectedUdtUrl = $udtCase.udtExpectedUrl ? $udtCase.udtExpectedUrl : ''
710701
link = $udtCase.link
711702
}
712703
}
713704
}
714705

715-
It '[<moduleFolderName>] If template has a parameter [<parameterName>], it should implement the user-defined type [<udtName>]' -TestCases $udtTestCases {
706+
It '[<moduleFolderName>] If template has a parameter [<parameterName>], it should implement AVM''s corresponding user-defined type.' -TestCases $udtTestCases {
716707

717708
param(
718709
[hashtable] $templateFileContent,
719710
[string[]] $templateFileContentBicep,
720711
[string] $parameterName,
721-
[string] $udtName,
722-
[string] $expectedUdtUrl,
723712
[string] $link
724713
)
725714

726715
if ($templateFileContent.parameters.Keys -contains $parameterName) {
727-
$templateFileContent.parameters.$parameterName.Keys | Should -Contain '$ref' -Because "the [$parameterName] parameter should use a user-defined type. For information please review the [AVM Specs]($link)."
728-
$templateFileContent.parameters.$parameterName.'$ref' | Should -Be "#/definitions/$udtName" -Because "the [$parameterName] parameter should use a user-defined type [$udtName]. For information please review the [AVM Specs]($link)."
729-
730-
if (-not [String]::IsNullOrEmpty($expectedUdtUrl)) {
731-
$implementedSchemaStartIndex = $templateFileContentBicep.IndexOf("type $udtName = {")
732-
$implementedSchemaEndIndex = $implementedSchemaStartIndex + 1
733-
while ($templateFileContentBicep[$implementedSchemaEndIndex] -notmatch '^\}.*' -and $implementedSchemaEndIndex -lt $templateFileContentBicep.Length) {
734-
$implementedSchemaEndIndex++
735-
}
736-
if ($implementedSchemaEndIndex -eq $templateFileContentBicep.Length) {
737-
throw "Failed to identify [$udtName] user-defined type in template."
738-
}
739-
$implementedSchema = $templateFileContentBicep[$implementedSchemaStartIndex..$implementedSchemaEndIndex]
740-
741-
try {
742-
$rawResponse = Invoke-WebRequest -Uri $expectedUdtUrl
743-
if (($rawResponse.Headers['Content-Type'] | Out-String) -like '*text/plain*') {
744-
$expectedSchemaFull = $rawResponse.Content -split '\n'
745-
} else {
746-
throw "Failed to fetch schema from [$expectedUdtUrl]. Skipping schema check"
747-
}
748-
} catch {
749-
Write-Warning "Failed to fetch schema from [$expectedUdtUrl]. Skipping schema check"
750-
return
751-
}
752-
753-
$expectedSchemaStartIndex = $expectedSchemaFull.IndexOf("type $udtName = {")
754-
$expectedSchemaEndIndex = $expectedSchemaStartIndex + 1
755-
while ($expectedSchemaFull[$expectedSchemaEndIndex] -notmatch '^\}.*' -and $expectedSchemaEndIndex -lt $expectedSchemaFull.Length) {
756-
$expectedSchemaEndIndex++
757-
}
758-
if ($expectedSchemaEndIndex -eq $expectedSchemaFull.Length) {
759-
throw "Failed to identify [$udtName] user-defined type in expected schema at URL [$expectedUdtUrl]."
760-
}
761-
$expectedSchema = $expectedSchemaFull[$expectedSchemaStartIndex..$expectedSchemaEndIndex]
762716

763-
if ($templateFileContentBicep -match '@sys\.([a-zA-Z]+)\(') {
764-
# Handing cases where the template may use the @sys namespace explicitly
765-
$expectedSchema = $expectedSchema | ForEach-Object { $_ -replace '@([a-zA-Z]+)\(', '@sys.$1(' }
766-
}
767-
768-
$formattedDiff = @()
769-
foreach ($finding in (Compare-Object $implementedSchema $expectedSchema)) {
770-
if ($finding.SideIndicator -eq '=>') {
771-
$formattedDiff += ('+ {0}' -f $finding.InputObject)
772-
} elseif ($finding.SideIndicator -eq '<=') {
773-
$formattedDiff += ('- {0}' -f $finding.InputObject)
774-
}
775-
}
776-
777-
if ($formattedDiff.Count -gt 0) {
778-
$warningMessage = "The implemented user-defined type is not the same as the expected user-defined type ({0}) defined in the AVM specs ({1}) and should not have diff`n{2}" -f $expectedUdtUrl, $link, ($formattedDiff | Out-String)
779-
Write-Warning $warningMessage
780-
781-
# Adding also to output to show in GitHub CI
782-
$mdFormattedDiff = ($formattedDiff -join '</br>') -replace '\|', '\|'
783-
$mdFormattedWarningMessage = 'The implemented user-defined type is not the same as the expected [user-defined type]({0}) defined in the [AVM specs]({1}) and should not have diff</br><pre>{2}</pre>' -f $expectedUdtUrl, $link, $mdFormattedDiff
784-
Write-Output @{
785-
Warning = $mdFormattedWarningMessage
786-
}
787-
}
717+
if ($templateFileContent.parameters.$parameterName.Keys -contains 'items') {
718+
# If parameter is an array, the UDT may focus on each element
719+
$templateFileContent.parameters.$parameterName.items.Keys | Should -Contain '$ref' -Because "the [$parameterName] parameter should use a user-defined type. For information please review the [AVM Specs]($link)."
720+
} else {
721+
# If not, the parameter itself should reference a UDT
722+
$templateFileContent.parameters.$parameterName.Keys | Should -Contain '$ref' -Because "the [$parameterName] parameter should use a user-defined type. For information please review the [AVM Specs]($link)."
788723
}
789724
} else {
790725
Set-ItResult -Skipped -Because "the module template has no [$parameterName] parameter."

0 commit comments

Comments
 (0)