From 50f5567546c5197567fe838d9646864b0beb4d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Thu, 12 Mar 2026 21:20:51 +0100 Subject: [PATCH 01/15] import mar file --- .github/workflows/mirror-mar-file.yml | 48 ++++++++++ .../tools/platform/Get-ModuleNamesFromMAR.ps1 | 88 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 .github/workflows/mirror-mar-file.yml create mode 100644 utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 diff --git a/.github/workflows/mirror-mar-file.yml b/.github/workflows/mirror-mar-file.yml new file mode 100644 index 000000000..583e5dcc4 --- /dev/null +++ b/.github/workflows/mirror-mar-file.yml @@ -0,0 +1,48 @@ +# Workflow for mirroring the MAR file to check Bicep modules against. +name: Mirror MAR File + +on: + # Runs everyday at 4 am + push: + branches: + - main + schedule: + - cron: "0 4 * * *" # Daily Update at 4 am + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: {} + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + id-token: write + contents: read + +# Default to bash +defaults: + run: + shell: bash + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout tag + uses: actions/checkout@v5 + + # create a token + - uses: actions/create-github-app-token@v2 + id: app-token + with: + owner: ${{ vars.MAR_REPO_OWNER}} + repositories: ${{ vars.MAR_REPO_REPOSITORY }} + app-id: ${{ vars.MAR_REPO_ACCESS_APPID }} + private-key: ${{ secrets.MAR_REPO_ACCESS_APP_PRIVATEKEY }} + # ensure the module is in the MAR file before publishing + - name: Confirm module in MAR + shell: pwsh + run: | + . './utilities/pipelines/publish/Confirm-ModuleInMAR.ps1' + $marModuleList = Confirm-ModuleInMAR -GitHubToken ${{ steps.app-token.outputs.token }} -Owner ${{ vars.MAR_REPO_OWNER}} -Repo ${{ vars.MAR_REPO_REPOSITORY }} + $localMARFilePath = Join-Path $env:GITHUB_WORKSPACE 'docs\static\module-indexes\BicepMARModules.json' + Set-LocalMARFileContent -LocalMARFilePath $localMARFilePath -FileContent $marModuleList diff --git a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 new file mode 100644 index 000000000..9834268f8 --- /dev/null +++ b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 @@ -0,0 +1,88 @@ +<# +.SYNOPSIS +Check if a module in a given path does exist in the MAR file. Returns $true or $false. + +.DESCRIPTION +Check if a module in a given path does exist in the MAR file. Only then, it can be published to the MCR. + +.PARAMETER GitHubToken +Mandatory. A GitHub token with read access to the MAR repository (microsoft/mcr). + +.PARAMETER Owner +Mandatory. The Organisation, where the repository is located. Default is "Microsoft". + +.PARAMETER Repo +Mandatory. The repository name. Default is "mcr". + +.EXAMPLE +Get-ModuleNamesFromMAR -GitHubToken $env:MAR_REPO_ACCESS_PAT -Owner 'Microsoft' -Repo 'mcr' -Verbose + +#> +function Get-ModuleNamesFromMAR { + + [CmdletBinding()] + param ( + [Parameter(Mandatory, HelpMessage = 'Provide a GitHub token (PAT for testing or GitHub App token for production) with read access to the MAR repository (microsoft/mcr).')] + [string] $GitHubToken, + + [Parameter(Mandatory, HelpMessage = 'Provide the Organisation, where the repository is located. Default is "Microsoft".')] + [string] $Owner, + + [Parameter(Mandatory, HelpMessage = 'Provide the repository name. Default is "mcr".')] + [string] $Repo + ) + + ################################## + ## Confirm module tag known ## + ################################## + $marFileUrl = "https://raw.githubusercontent.com/$Owner/$Repo/refs/heads/main/teams/bicep/bicep.yml" + $headers = @{ + Authorization = "Bearer $GitHubToken" + Accept = 'application/vnd.github.v3.raw' + 'User-Agent' = 'AVM-Publish-Pipeline' + } + + $marFileContent = $null + + try { + $marFileContent = (Invoke-WebRequest -Uri $marFileUrl -Headers $headers -ErrorAction Stop).Content + } + catch { + throw "Failed to fetch MAR file from [$marFileUrl]. Error: $($_.Exception.Message)" + } + + $marFileModuleNames = @() + try { + # if (Get-Command -Name 'ConvertFrom-Yaml' -ErrorAction SilentlyContinue) { + # $marObject = $marFileContent | ConvertFrom-Yaml -ErrorAction Stop + # $marFileModuleNames = @($marObject.repos | Where-Object { $null -ne $_.name } | ForEach-Object { $_.name -replace '^public/bicep/', '' }) + # } + + $marFileModuleNames = @([regex]::Matches($marFileContent, '(?m)^\s*-\s*name:\s*(?[^\r\n]+)') | ForEach-Object { + $_.Groups['name'].Value.Trim() -replace '^public/bicep/', '' + }) + } + catch { + throw "Failed to parse module names from MAR file. Error: $($_.Exception.Message)" + } + + return $marFileModuleNames +} + +function Set-LocalMARFileContent { + [CmdletBinding()] + param ( + [Parameter(Mandatory, HelpMessage = 'Provide the path to the local MAR file copy.')] + [string] $LocalMARFilePath, + + [Parameter(Mandatory, HelpMessage = 'Provide the new content for the local MAR file.')] + [string[]] $FileContent + ) + + if ($PSCmdlet.ShouldProcess("File in path [$LocalMARFilePath]", 'Overwrite')) { + $jsonContent = $FileContent | ConvertTo-Json + # Set-Content -Path $LocalMARFilePath -Value $jsonContent -Encoding utf8 -Force + New-Item -Path $LocalMARFilePath -Value ($jsonContent | Out-String) -Force + Write-Verbose "File [$LocalMARFilePath] updated" -Verbose + } +} From 0160148e68cb5ac23650c8f14c9e73bfa646d492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Tue, 24 Mar 2026 16:57:44 +0100 Subject: [PATCH 02/15] implementation --- .github/workflows/mirror-mar-file.yml | 7 +++++-- .../tools/platform/Get-ModuleNamesFromMAR.ps1 | 16 +++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/workflows/mirror-mar-file.yml b/.github/workflows/mirror-mar-file.yml index 583e5dcc4..0736ae7cd 100644 --- a/.github/workflows/mirror-mar-file.yml +++ b/.github/workflows/mirror-mar-file.yml @@ -42,7 +42,10 @@ jobs: - name: Confirm module in MAR shell: pwsh run: | - . './utilities/pipelines/publish/Confirm-ModuleInMAR.ps1' - $marModuleList = Confirm-ModuleInMAR -GitHubToken ${{ steps.app-token.outputs.token }} -Owner ${{ vars.MAR_REPO_OWNER}} -Repo ${{ vars.MAR_REPO_REPOSITORY }} + . './utilities/tools/platform/Get-ModuleNamesFromMAR.ps1' + # get the list of module names in the MAR file as string array + $marModuleList = Get-ModuleNamesFromMAR -GitHubToken ${{ steps.app-token.outputs.token }} -Owner ${{ vars.MAR_REPO_OWNER}} -Repo ${{ vars.MAR_REPO_REPOSITORY }} + + # set the content as JSON of the local MAR file to the list of module names retrieved from the MAR file in the repository $localMARFilePath = Join-Path $env:GITHUB_WORKSPACE 'docs\static\module-indexes\BicepMARModules.json' Set-LocalMARFileContent -LocalMARFilePath $localMARFilePath -FileContent $marModuleList diff --git a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 index 9834268f8..dea750ec4 100644 --- a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 +++ b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 @@ -17,6 +17,8 @@ Mandatory. The repository name. Default is "mcr". .EXAMPLE Get-ModuleNamesFromMAR -GitHubToken $env:MAR_REPO_ACCESS_PAT -Owner 'Microsoft' -Repo 'mcr' -Verbose +Returns the list of module names in the MAR file as an array of strings. + #> function Get-ModuleNamesFromMAR { @@ -53,11 +55,6 @@ function Get-ModuleNamesFromMAR { $marFileModuleNames = @() try { - # if (Get-Command -Name 'ConvertFrom-Yaml' -ErrorAction SilentlyContinue) { - # $marObject = $marFileContent | ConvertFrom-Yaml -ErrorAction Stop - # $marFileModuleNames = @($marObject.repos | Where-Object { $null -ne $_.name } | ForEach-Object { $_.name -replace '^public/bicep/', '' }) - # } - $marFileModuleNames = @([regex]::Matches($marFileContent, '(?m)^\s*-\s*name:\s*(?[^\r\n]+)') | ForEach-Object { $_.Groups['name'].Value.Trim() -replace '^public/bicep/', '' }) @@ -79,10 +76,7 @@ function Set-LocalMARFileContent { [string[]] $FileContent ) - if ($PSCmdlet.ShouldProcess("File in path [$LocalMARFilePath]", 'Overwrite')) { - $jsonContent = $FileContent | ConvertTo-Json - # Set-Content -Path $LocalMARFilePath -Value $jsonContent -Encoding utf8 -Force - New-Item -Path $LocalMARFilePath -Value ($jsonContent | Out-String) -Force - Write-Verbose "File [$LocalMARFilePath] updated" -Verbose - } + $jsonContent = $FileContent | ConvertTo-Json + New-Item -Path $LocalMARFilePath -Value ($jsonContent | Out-String) -Force + Write-Verbose "File [$LocalMARFilePath] updated" -Verbose } From 501468797cddf400b4608b201a4d603cd245ecf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Tue, 24 Mar 2026 16:57:52 +0100 Subject: [PATCH 03/15] mar file --- .../module-indexes/BicepMARModules.json | 338 ++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 docs/static/module-indexes/BicepMARModules.json diff --git a/docs/static/module-indexes/BicepMARModules.json b/docs/static/module-indexes/BicepMARModules.json new file mode 100644 index 000000000..50ca0c9fb --- /dev/null +++ b/docs/static/module-indexes/BicepMARModules.json @@ -0,0 +1,338 @@ +[ + "avm/ptn/aca-lza/hosting-environment", + "avm/ptn/ai-ml/ai-foundry", + "avm/ptn/ai-ml/landing-zone", + "avm/ptn/ai-platform/baseline", + "avm/ptn/alz/ama", + "avm/ptn/alz/empty", + "avm/ptn/app-service-lza/hosting-environment", + "avm/ptn/app/container-job-toolkit", + "avm/ptn/app/cosmos-db-account-container-app", + "avm/ptn/app/iaas-vm-cosmosdb-tier4", + "avm/ptn/app/mongodb-cluster-container-app", + "avm/ptn/app/paas-ase-cosmosdb-tier4", + "avm/ptn/authorization/pim-role-assignment", + "avm/ptn/authorization/policy-assignment", + "avm/ptn/authorization/policy-exemption", + "avm/ptn/authorization/resource-role-assignment", + "avm/ptn/authorization/role-assignment", + "avm/ptn/authorization/role-definition", + "avm/ptn/avd-lza/insights", + "avm/ptn/avd-lza/management-plane", + "avm/ptn/avd-lza/networking", + "avm/ptn/avd-lza/session-hosts", + "avm/ptn/azd/acr-container-app", + "avm/ptn/azd/aks", + "avm/ptn/azd/aks-automatic-cluster", + "avm/ptn/azd/apim-api", + "avm/ptn/azd/container-app-upsert", + "avm/ptn/azd/container-apps-stack", + "avm/ptn/azd/insights-dashboard", + "avm/ptn/azd/ml-ai-environment", + "avm/ptn/azd/ml-hub-dependencies", + "avm/ptn/azd/ml-project", + "avm/ptn/azd/monitoring", + "avm/ptn/data/private-analytical-workspace", + "avm/ptn/deployment-script/create-kv-ssh-key-pair", + "avm/ptn/deployment-script/import-image-to-acr", + "avm/ptn/deployment-script/private", + "avm/ptn/dev-center/dev-box", + "avm/ptn/dev-ops/cicd-agents-and-runners", + "avm/ptn/finops-toolkit/finops-hub", + "avm/ptn/lz/sub-vending", + "avm/ptn/lza-shared/data-services", + "avm/ptn/maintenance/azure-update-manager", + "avm/ptn/mgmt-groups/subscription-placement", + "avm/ptn/monitoring/amba", + "avm/ptn/monitoring/amba-alz", + "avm/ptn/network/hub-networking", + "avm/ptn/network/private-link-private-dns-zones", + "avm/ptn/network/virtual-wan", + "avm/ptn/network/vwan-connected-vnets", + "avm/ptn/openai/cognitive-search", + "avm/ptn/openai/e2e-baseline", + "avm/ptn/policy-insights/remediation", + "avm/ptn/sa/build-your-own-copilot", + "avm/ptn/sa/chat-with-your-data", + "avm/ptn/sa/content-generation", + "avm/ptn/sa/content-processing", + "avm/ptn/sa/conversation-knowledge-mining", + "avm/ptn/sa/customer-chatbot", + "avm/ptn/sa/document-knowledge-mining", + "avm/ptn/sa/modernize-your-code", + "avm/ptn/sa/multi-agent-custom-automation-engine", + "avm/ptn/security/security-center", + "avm/ptn/security/sentinel", + "avm/ptn/subscription/service-health-alerts", + "avm/ptn/virtual-machine-images/azure-image-builder", + "avm/res/aad/domain-service", + "avm/res/alerts-management/action-rule", + "avm/res/analysis-services/server", + "avm/res/api-center/service", + "avm/res/api-management/service", + "avm/res/api-management/service/api", + "avm/res/api-management/service/api-version-set", + "avm/res/api-management/service/api/diagnostics", + "avm/res/api-management/service/api/policy", + "avm/res/api-management/service/authorization-server", + "avm/res/api-management/service/backend", + "avm/res/api-management/service/cache", + "avm/res/api-management/service/diagnostics", + "avm/res/api-management/service/identity-provider", + "avm/res/api-management/service/logger", + "avm/res/api-management/service/named-value", + "avm/res/api-management/service/policy", + "avm/res/api-management/service/portalsetting", + "avm/res/api-management/service/private-endpoint-connection", + "avm/res/api-management/service/product", + "avm/res/api-management/service/product/api", + "avm/res/api-management/service/product/group", + "avm/res/api-management/service/subscription", + "avm/res/api-management/service/workspace", + "avm/res/app-configuration/configuration-store", + "avm/res/app/agent", + "avm/res/app/container-app", + "avm/res/app/job", + "avm/res/app/managed-environment", + "avm/res/app/session-pool", + "avm/res/authorization/policy-assignment", + "avm/res/authorization/policy-assignment/mg-scope", + "avm/res/authorization/policy-assignment/rg-scope", + "avm/res/authorization/policy-assignment/sub-scope", + "avm/res/authorization/role-assignment", + "avm/res/authorization/role-assignment/mg-scope", + "avm/res/authorization/role-assignment/rg-scope", + "avm/res/authorization/role-assignment/sub-scope", + "avm/res/automation/automation-account", + "avm/res/avs/private-cloud", + "avm/res/azure-stack-hci/cluster", + "avm/res/azure-stack-hci/cluster/arc-setting/extension", + "avm/res/azure-stack-hci/logical-network", + "avm/res/azure-stack-hci/marketplace-gallery-image", + "avm/res/azure-stack-hci/network-interface", + "avm/res/azure-stack-hci/virtual-hard-disk", + "avm/res/azure-stack-hci/virtual-machine-instance", + "avm/res/batch/batch-account", + "avm/res/bot-service/bot-service", + "avm/res/cache/redis", + "avm/res/cache/redis-enterprise", + "avm/res/cdn/profile", + "avm/res/cdn/profile/afd-endpoint/route", + "avm/res/cdn/profile/custom-domain", + "avm/res/cdn/profile/origin-group", + "avm/res/cdn/profile/rule-set", + "avm/res/cdn/profile/security-policy", + "avm/res/chaos/experiment", + "avm/res/code-signing/code-signing-account", + "avm/res/cognitive-services/account", + "avm/res/communication/communication-service", + "avm/res/communication/email-service", + "avm/res/compute/availability-set", + "avm/res/compute/disk", + "avm/res/compute/disk-encryption-set", + "avm/res/compute/gallery", + "avm/res/compute/image", + "avm/res/compute/proximity-placement-group", + "avm/res/compute/ssh-public-key", + "avm/res/compute/virtual-machine", + "avm/res/compute/virtual-machine-scale-set", + "avm/res/consumption/budget", + "avm/res/consumption/budget/mg-scope", + "avm/res/consumption/budget/rg-scope", + "avm/res/consumption/budget/sub-scope", + "avm/res/container-instance/container-group", + "avm/res/container-registry/registry", + "avm/res/container-registry/registry/cache-rule", + "avm/res/container-registry/registry/credential-set", + "avm/res/container-registry/registry/replication", + "avm/res/container-registry/registry/scope-map", + "avm/res/container-registry/registry/task", + "avm/res/container-registry/registry/token", + "avm/res/container-registry/registry/webhook", + "avm/res/container-service/managed-cluster", + "avm/res/dashboard/grafana", + "avm/res/data-factory/factory", + "avm/res/data-protection/backup-vault", + "avm/res/data-protection/resource-guard", + "avm/res/databricks/access-connector", + "avm/res/databricks/workspace", + "avm/res/db-for-my-sql/flexible-server", + "avm/res/db-for-postgre-sql/flexible-server", + "avm/res/desktop-virtualization/application-group", + "avm/res/desktop-virtualization/host-pool", + "avm/res/desktop-virtualization/scaling-plan", + "avm/res/desktop-virtualization/workspace", + "avm/res/dev-center/devcenter", + "avm/res/dev-center/network-connection", + "avm/res/dev-center/project", + "avm/res/dev-ops-infrastructure/pool", + "avm/res/dev-test-lab/lab", + "avm/res/devices/iot-hub", + "avm/res/digital-twins/digital-twins-instance", + "avm/res/document-db/database-account", + "avm/res/document-db/database-account/sql-database", + "avm/res/document-db/database-account/sql-role-assignment", + "avm/res/document-db/database-account/sql-role-definition", + "avm/res/document-db/database-account/table", + "avm/res/document-db/mongo-cluster", + "avm/res/durable-task/scheduler", + "avm/res/edge-order/order-item", + "avm/res/edge/configuration", + "avm/res/edge/site", + "avm/res/edge/site/rg-scope", + "avm/res/edge/site/sub-scope", + "avm/res/elastic-san/elastic-san", + "avm/res/event-grid/domain", + "avm/res/event-grid/namespace", + "avm/res/event-grid/system-topic", + "avm/res/event-grid/topic", + "avm/res/event-hub/namespace", + "avm/res/event-hub/namespace/eventhub", + "avm/res/fabric/capacity", + "avm/res/health-bot/health-bot", + "avm/res/healthcare-apis/workspace", + "avm/res/hybrid-compute/gateway", + "avm/res/hybrid-compute/license", + "avm/res/hybrid-compute/machine", + "avm/res/hybrid-compute/private-link-scope", + "avm/res/hybrid-compute/setting", + "avm/res/hybrid-container-service/provisioned-cluster-instance", + "avm/res/insights/action-group", + "avm/res/insights/activity-log-alert", + "avm/res/insights/autoscale-setting", + "avm/res/insights/component", + "avm/res/insights/data-collection-endpoint", + "avm/res/insights/data-collection-rule", + "avm/res/insights/diagnostic-setting", + "avm/res/insights/metric-alert", + "avm/res/insights/private-link-scope", + "avm/res/insights/scheduled-query-rule", + "avm/res/insights/webtest", + "avm/res/iot-operations/instance", + "avm/res/key-vault/managed-hsm", + "avm/res/key-vault/vault", + "avm/res/key-vault/vault/access-policy", + "avm/res/key-vault/vault/key", + "avm/res/key-vault/vault/secret", + "avm/res/kubernetes-configuration/extension", + "avm/res/kubernetes-configuration/flux-configuration", + "avm/res/kubernetes-runtime/bpg-peer", + "avm/res/kubernetes-runtime/load-balancer", + "avm/res/kubernetes-runtime/service", + "avm/res/kubernetes/connected-cluster", + "avm/res/kusto/cluster", + "avm/res/load-test-service/load-test", + "avm/res/logic/integration-account", + "avm/res/logic/workflow", + "avm/res/machine-learning-services/registry", + "avm/res/machine-learning-services/workspace", + "avm/res/maintenance/configuration-assignment", + "avm/res/maintenance/maintenance-configuration", + "avm/res/managed-identity/user-assigned-identity", + "avm/res/managed-services/registration-definition", + "avm/res/management/management-group", + "avm/res/management/service-group", + "avm/res/maps/account", + "avm/res/net-app/net-app-account", + "avm/res/network/application-gateway", + "avm/res/network/application-gateway-web-application-firewall-policy", + "avm/res/network/application-security-group", + "avm/res/network/azure-firewall", + "avm/res/network/bastion-host", + "avm/res/network/connection", + "avm/res/network/ddos-protection-plan", + "avm/res/network/dns-forwarding-ruleset", + "avm/res/network/dns-resolver", + "avm/res/network/dns-zone", + "avm/res/network/express-route-circuit", + "avm/res/network/express-route-gateway", + "avm/res/network/express-route-port", + "avm/res/network/firewall-policy", + "avm/res/network/front-door", + "avm/res/network/front-door-web-application-firewall-policy", + "avm/res/network/ip-group", + "avm/res/network/load-balancer", + "avm/res/network/local-network-gateway", + "avm/res/network/nat-gateway", + "avm/res/network/network-interface", + "avm/res/network/network-manager", + "avm/res/network/network-security-group", + "avm/res/network/network-security-perimeter", + "avm/res/network/network-watcher", + "avm/res/network/p2s-vpn-gateway", + "avm/res/network/private-dns-zone", + "avm/res/network/private-dns-zone/a", + "avm/res/network/private-dns-zone/aaaa", + "avm/res/network/private-dns-zone/cname", + "avm/res/network/private-dns-zone/mx", + "avm/res/network/private-dns-zone/ptr", + "avm/res/network/private-dns-zone/soa", + "avm/res/network/private-dns-zone/srv", + "avm/res/network/private-dns-zone/txt", + "avm/res/network/private-dns-zone/virtual-network-link", + "avm/res/network/private-endpoint", + "avm/res/network/private-link-service", + "avm/res/network/public-ip-address", + "avm/res/network/public-ip-prefix", + "avm/res/network/route-table", + "avm/res/network/service-endpoint-policy", + "avm/res/network/trafficmanagerprofile", + "avm/res/network/virtual-hub", + "avm/res/network/virtual-hub/route-map", + "avm/res/network/virtual-network", + "avm/res/network/virtual-network-gateway", + "avm/res/network/virtual-network/subnet", + "avm/res/network/virtual-wan", + "avm/res/network/vpn-gateway", + "avm/res/network/vpn-server-configuration", + "avm/res/network/vpn-site", + "avm/res/operational-insights/cluster", + "avm/res/operational-insights/workspace", + "avm/res/operations-management/solution", + "avm/res/portal/dashboard", + "avm/res/power-bi-dedicated/capacity", + "avm/res/purview/account", + "avm/res/recovery-services/vault", + "avm/res/relay/namespace", + "avm/res/resource-graph/query", + "avm/res/resources/deployment-script", + "avm/res/resources/resource-group", + "avm/res/scom/managed-instance", + "avm/res/search/search-service", + "avm/res/security-insights/data-connector", + "avm/res/security-insights/onboarding-state", + "avm/res/security-insights/setting", + "avm/res/service-bus/namespace", + "avm/res/service-bus/namespace/queue", + "avm/res/service-bus/namespace/topic", + "avm/res/service-fabric/cluster", + "avm/res/service-networking/traffic-controller", + "avm/res/signal-r-service/signal-r", + "avm/res/signal-r-service/web-pub-sub", + "avm/res/sql-virtual-machine/sql-virtual-machine", + "avm/res/sql/instance-pool", + "avm/res/sql/managed-instance", + "avm/res/sql/server", + "avm/res/sql/server/database", + "avm/res/storage/storage-account", + "avm/res/storage/storage-account/blob-service/container", + "avm/res/storage/storage-account/blob-service/container/immutability-policy", + "avm/res/storage/storage-account/file-service/share", + "avm/res/storage/storage-account/local-user", + "avm/res/storage/storage-account/management-policy", + "avm/res/storage/storage-account/queue-service/queue", + "avm/res/storage/storage-account/table-service/table", + "avm/res/stream-analytics/streaming-job", + "avm/res/synapse/private-link-hub", + "avm/res/synapse/workspace", + "avm/res/virtual-machine-images/image-template", + "avm/res/web/connection", + "avm/res/web/hosting-environment", + "avm/res/web/serverfarm", + "avm/res/web/site", + "avm/res/web/site/config", + "avm/res/web/site/slot", + "avm/res/web/static-site", + "avm/utl/general/get-environment", + "avm/utl/types/avm-common-types" +] From ba5e28ae9b20592fae1ead79cb1c270d98e0a6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Tue, 24 Mar 2026 18:18:30 +0100 Subject: [PATCH 04/15] creates a pr for the modified file --- .github/workflows/mirror-mar-file.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/mirror-mar-file.yml b/.github/workflows/mirror-mar-file.yml index 0736ae7cd..848e7b30f 100644 --- a/.github/workflows/mirror-mar-file.yml +++ b/.github/workflows/mirror-mar-file.yml @@ -15,7 +15,7 @@ on: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: id-token: write - contents: read + contents: write # Default to bash defaults: @@ -29,6 +29,9 @@ jobs: steps: - name: Checkout tag uses: actions/checkout@v5 + with: + fetch-depth: 0 + ref: ${{ github.ref_name }} # create a token - uses: actions/create-github-app-token@v2 @@ -49,3 +52,23 @@ jobs: # set the content as JSON of the local MAR file to the list of module names retrieved from the MAR file in the repository $localMARFilePath = Join-Path $env:GITHUB_WORKSPACE 'docs\static\module-indexes\BicepMARModules.json' Set-LocalMARFileContent -LocalMARFilePath $localMARFilePath -FileContent $marModuleList + + - name: Check for changes + id: git_status + shell: pwsh + run: | + $diff = git diff --name-only -- 'docs/static/module-indexes/BicepMARModules.json' + if ($diff.Count -gt 0) { + Write-Verbose 'Detected updates to mirrored MAR file.' -Verbose + 'changes=true' >> $env:GITHUB_OUTPUT + } + + - name: Commit and push mirrored MAR file + if: steps.git_status.outputs.changes == 'true' + shell: pwsh + run: | + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add 'docs/static/module-indexes/BicepMARModules.json' + git commit -m 'Update mirrored MAR file' + git push origin HEAD:${{ github.ref_name }} From 8b64c22f3c59c112a4a7d689d8f1a019b861d8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Tue, 24 Mar 2026 18:18:47 +0100 Subject: [PATCH 05/15] addresses Linter warnings --- utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 index dea750ec4..ddbf0e7f1 100644 --- a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 +++ b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 @@ -23,6 +23,7 @@ Returns the list of module names in the MAR file as an array of strings. function Get-ModuleNamesFromMAR { [CmdletBinding()] + [OutputType([string[]])] param ( [Parameter(Mandatory, HelpMessage = 'Provide a GitHub token (PAT for testing or GitHub App token for production) with read access to the MAR repository (microsoft/mcr).')] [string] $GitHubToken, @@ -53,9 +54,9 @@ function Get-ModuleNamesFromMAR { throw "Failed to fetch MAR file from [$marFileUrl]. Error: $($_.Exception.Message)" } - $marFileModuleNames = @() + [string[]] $marFileModuleNames = @() try { - $marFileModuleNames = @([regex]::Matches($marFileContent, '(?m)^\s*-\s*name:\s*(?[^\r\n]+)') | ForEach-Object { + [string[]] $marFileModuleNames = @([regex]::Matches($marFileContent, '(?m)^\s*-\s*name:\s*(?[^\r\n]+)') | ForEach-Object { $_.Groups['name'].Value.Trim() -replace '^public/bicep/', '' }) } @@ -67,7 +68,7 @@ function Get-ModuleNamesFromMAR { } function Set-LocalMARFileContent { - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory, HelpMessage = 'Provide the path to the local MAR file copy.')] [string] $LocalMARFilePath, @@ -77,6 +78,8 @@ function Set-LocalMARFileContent { ) $jsonContent = $FileContent | ConvertTo-Json - New-Item -Path $LocalMARFilePath -Value ($jsonContent | Out-String) -Force - Write-Verbose "File [$LocalMARFilePath] updated" -Verbose + if ($PSCmdlet.ShouldProcess($LocalMARFilePath, 'Update local MAR file content')) { + $null = New-Item -Path $LocalMARFilePath -Value ($jsonContent | Out-String) -Force + Write-Verbose "File [$LocalMARFilePath] updated" -Verbose + } } From d2f148fefc86bef1d00ec3a02a69bd46677c2e1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Tue, 24 Mar 2026 18:23:59 +0100 Subject: [PATCH 06/15] make Linter happy --- utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 index ddbf0e7f1..a3745d279 100644 --- a/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 +++ b/utilities/tools/platform/Get-ModuleNamesFromMAR.ps1 @@ -54,17 +54,18 @@ function Get-ModuleNamesFromMAR { throw "Failed to fetch MAR file from [$marFileUrl]. Error: $($_.Exception.Message)" } - [string[]] $marFileModuleNames = @() + $marFileModuleNames = [System.Collections.Generic.List[string]]::new() try { - [string[]] $marFileModuleNames = @([regex]::Matches($marFileContent, '(?m)^\s*-\s*name:\s*(?[^\r\n]+)') | ForEach-Object { - $_.Groups['name'].Value.Trim() -replace '^public/bicep/', '' - }) + foreach ($match in [regex]::Matches($marFileContent, '(?m)^\s*-\s*name:\s*(?[^\r\n]+)')) { + $moduleName = $match.Groups['name'].Value.Trim() -replace '^public/bicep/', '' + $null = $marFileModuleNames.Add($moduleName) + } } catch { throw "Failed to parse module names from MAR file. Error: $($_.Exception.Message)" } - return $marFileModuleNames + return $marFileModuleNames.ToArray() } function Set-LocalMARFileContent { From 6ffc183ee550ff9bbe55dc4f133f79083fb5a41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 07:47:58 +0100 Subject: [PATCH 07/15] action name and update --- .../{mirror-mar-file.yml => platform.mirror-mar-file.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{mirror-mar-file.yml => platform.mirror-mar-file.yml} (96%) diff --git a/.github/workflows/mirror-mar-file.yml b/.github/workflows/platform.mirror-mar-file.yml similarity index 96% rename from .github/workflows/mirror-mar-file.yml rename to .github/workflows/platform.mirror-mar-file.yml index 848e7b30f..5453a199f 100644 --- a/.github/workflows/mirror-mar-file.yml +++ b/.github/workflows/platform.mirror-mar-file.yml @@ -1,5 +1,5 @@ # Workflow for mirroring the MAR file to check Bicep modules against. -name: Mirror MAR File +name: ".Platform: Mirror MAR File" on: # Runs everyday at 4 am @@ -34,7 +34,7 @@ jobs: ref: ${{ github.ref_name }} # create a token - - uses: actions/create-github-app-token@v2 + - uses: actions/create-github-app-token@v3 id: app-token with: owner: ${{ vars.MAR_REPO_OWNER}} From bb6f3477b3c0ef00bbb62e04a00fed30728b236a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 07:51:19 +0100 Subject: [PATCH 08/15] removes push trigger --- .github/workflows/platform.mirror-mar-file.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/platform.mirror-mar-file.yml b/.github/workflows/platform.mirror-mar-file.yml index 5453a199f..5737a5062 100644 --- a/.github/workflows/platform.mirror-mar-file.yml +++ b/.github/workflows/platform.mirror-mar-file.yml @@ -3,9 +3,6 @@ name: ".Platform: Mirror MAR File" on: # Runs everyday at 4 am - push: - branches: - - main schedule: - cron: "0 4 * * *" # Daily Update at 4 am From d1d777c2fc5ff2dc7e7a0246711c4316fc8c4f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 07:54:17 +0100 Subject: [PATCH 09/15] testing --- docs/static/module-indexes/BicepMARModules.json | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/static/module-indexes/BicepMARModules.json b/docs/static/module-indexes/BicepMARModules.json index 50ca0c9fb..8ef13953b 100644 --- a/docs/static/module-indexes/BicepMARModules.json +++ b/docs/static/module-indexes/BicepMARModules.json @@ -65,7 +65,6 @@ "avm/ptn/security/sentinel", "avm/ptn/subscription/service-health-alerts", "avm/ptn/virtual-machine-images/azure-image-builder", - "avm/res/aad/domain-service", "avm/res/alerts-management/action-rule", "avm/res/analysis-services/server", "avm/res/api-center/service", From 605d2975e016d7bcae08bb3281cd708cc2eeaa79 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 06:54:55 +0000 Subject: [PATCH 10/15] Update mirrored MAR file --- docs/static/module-indexes/BicepMARModules.json | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/static/module-indexes/BicepMARModules.json b/docs/static/module-indexes/BicepMARModules.json index 8ef13953b..50ca0c9fb 100644 --- a/docs/static/module-indexes/BicepMARModules.json +++ b/docs/static/module-indexes/BicepMARModules.json @@ -65,6 +65,7 @@ "avm/ptn/security/sentinel", "avm/ptn/subscription/service-health-alerts", "avm/ptn/virtual-machine-images/azure-image-builder", + "avm/res/aad/domain-service", "avm/res/alerts-management/action-rule", "avm/res/analysis-services/server", "avm/res/api-center/service", From f250bc808652cea4e9da37b08c20cfd64ffcf1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 16:08:17 +0100 Subject: [PATCH 11/15] update action --- .../workflows/platform.mirror-mar-file.yml | 93 ++++++++++++++++--- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/.github/workflows/platform.mirror-mar-file.yml b/.github/workflows/platform.mirror-mar-file.yml index 5737a5062..53bc5e3c8 100644 --- a/.github/workflows/platform.mirror-mar-file.yml +++ b/.github/workflows/platform.mirror-mar-file.yml @@ -9,6 +9,13 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: {} +env: + pipelinePrincipalGitUserName: "AVMPipelinePrincipal" + pipelinePrincipalGitUserEmail: "AVM@noreply.github.com" + branch_name: "update-mar-file" + pr_title: "Update mirrored MAR file (automated)" + pr_body: "This is an automated ``pull_request`` containing updates to the mirrored MAR file stored at ``docs/static/module-indexes/BicepMARModules.json``.\nPlease review the ``files changed`` tab to review changes." + # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: id-token: write @@ -20,36 +27,75 @@ defaults: shell: bash jobs: - # Build job - build: + update_mar_file: + name: Update MAR file + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + environment: platform steps: - - name: Checkout tag + - name: Checkout uses: actions/checkout@v5 with: fetch-depth: 0 - ref: ${{ github.ref_name }} # create a token - uses: actions/create-github-app-token@v3 - id: app-token + id: mcr-app-token with: owner: ${{ vars.MAR_REPO_OWNER}} repositories: ${{ vars.MAR_REPO_REPOSITORY }} app-id: ${{ vars.MAR_REPO_ACCESS_APPID }} private-key: ${{ secrets.MAR_REPO_ACCESS_APP_PRIVATEKEY }} + + # - uses: actions/create-github-app-token@v3 + # id: avm-app-token + # with: + # app-id: ${{ secrets.APP_ID }} + # private-key: ${{ secrets.APP_PRIVATE_KEY }} + # ensure the module is in the MAR file before publishing - name: Confirm module in MAR shell: pwsh run: | . './utilities/tools/platform/Get-ModuleNamesFromMAR.ps1' # get the list of module names in the MAR file as string array - $marModuleList = Get-ModuleNamesFromMAR -GitHubToken ${{ steps.app-token.outputs.token }} -Owner ${{ vars.MAR_REPO_OWNER}} -Repo ${{ vars.MAR_REPO_REPOSITORY }} + $marModuleList = Get-ModuleNamesFromMAR -GitHubToken ${{ steps.mcr-app-token.outputs.token }} -Owner ${{ vars.MAR_REPO_OWNER}} -Repo ${{ vars.MAR_REPO_REPOSITORY }} # set the content as JSON of the local MAR file to the list of module names retrieved from the MAR file in the repository $localMARFilePath = Join-Path $env:GITHUB_WORKSPACE 'docs\static\module-indexes\BicepMARModules.json' Set-LocalMARFileContent -LocalMARFilePath $localMARFilePath -FileContent $marModuleList + - name: Configure local git + run: | + git config --global user.name '${{ env.pipelinePrincipalGitUserName }}' + git config --global user.email '${{ env.pipelinePrincipalGitUserEmail }}' + + - name: Format branch name + shell: pwsh + run: | + $rawBranch = '${{ env.branch_name }}' + $formattedBranch = "{0}_{1}" -f $rawBranch, (Get-Date).ToString('yyyy-MM-dd-HH-mm-ss') + Write-Verbose "Adjusting branch name [$rawBranch] to [$formattedBranch]" -Verbose + ('{0}={1}' -f 'branch_name', $formattedBranch) | Out-File -FilePath $env:GITHUB_ENV -Encoding 'utf8' # Overwrite env variable + + - name: Create and checkout branch + run: | + BRANCH_URL="repos/${{ github.repository }}/branches" + JQ_FILTER=".[] | select(.name == \"${{ env.branch_name }}\").name" + CHECK_BRANCH_ORIGIN=$(gh api $BRANCH_URL | jq -r "$JQ_FILTER") + if [ -z "$CHECK_BRANCH_ORIGIN" ] + then + echo "Checkout local branch (create new, no origin)..." + git checkout -b ${{ env.branch_name }} + else + echo "Checkout local branch (create new, track from origin)..." + git checkout -b ${{ env.branch_name }} --track origin/${{ env.branch_name }} + + git merge origin/main + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check for changes id: git_status shell: pwsh @@ -60,12 +106,35 @@ jobs: 'changes=true' >> $env:GITHUB_OUTPUT } - - name: Commit and push mirrored MAR file + - name: Add files, commit and push if: steps.git_status.outputs.changes == 'true' shell: pwsh run: | - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add 'docs/static/module-indexes/BicepMARModules.json' - git commit -m 'Update mirrored MAR file' - git push origin HEAD:${{ github.ref_name }} + Write-Verbose "Pushing changes to origin..." -Verbose + git add (Join-Path $env:GITHUB_WORKSPACE 'docs' 'static' 'module-indexes' 'BicepMARModules.json') + git commit -m '${{ env.pr_title }}' + git push origin ${{ env.branch_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create pull request + if: steps.git_status.outputs.changes == 'true' + run: | + HEAD_LABEL="${{ github.repository_owner }}:${{ env.branch_name }}" + BASE_LABEL="${{ github.repository_owner }}:$(echo '${{ github.ref }}' | sed 's:refs/heads/::')" + PULL_REQUEST_URL="repos/${{ github.repository }}/pulls" + JQ_FILTER=".[] | select(.head.label == \"$HEAD_LABEL\") | select(.base.label == \"$BASE_LABEL\") | .url" + CHECK_PULL_REQUEST_URL=$(gh api $PULL_REQUEST_URL | jq -r "$JQ_FILTER") + if [ -z "$CHECK_PULL_REQUEST_URL" ] + then + CHECK_PULL_REQUEST_URL=$(gh pr create \ + --title "${{ env.pr_title }}" \ + --body "${{ env.pr_body }}" \ + --base "${{ github.ref }}" \ + --head "${{ env.branch_name }}") + echo "Created new PR: $CHECK_PULL_REQUEST_URL" + else + echo "Existing PR found: $CHECK_PULL_REQUEST_URL" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9a6f8a67e633a1be8f17bb4a9625b429c70a5705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 16:11:58 +0100 Subject: [PATCH 12/15] preparing testing --- docs/static/module-indexes/BicepMARModules.json | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/static/module-indexes/BicepMARModules.json b/docs/static/module-indexes/BicepMARModules.json index 50ca0c9fb..8ef13953b 100644 --- a/docs/static/module-indexes/BicepMARModules.json +++ b/docs/static/module-indexes/BicepMARModules.json @@ -65,7 +65,6 @@ "avm/ptn/security/sentinel", "avm/ptn/subscription/service-health-alerts", "avm/ptn/virtual-machine-images/azure-image-builder", - "avm/res/aad/domain-service", "avm/res/alerts-management/action-rule", "avm/res/analysis-services/server", "avm/res/api-center/service", From 69ce5bc6f96fad5616c9cf5c228002a6ba354ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 16:13:28 +0100 Subject: [PATCH 13/15] fix action permissions --- .github/workflows/platform.mirror-mar-file.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/platform.mirror-mar-file.yml b/.github/workflows/platform.mirror-mar-file.yml index 53bc5e3c8..612f3eaf5 100644 --- a/.github/workflows/platform.mirror-mar-file.yml +++ b/.github/workflows/platform.mirror-mar-file.yml @@ -20,6 +20,7 @@ env: permissions: id-token: write contents: write + pull-requests: write # Default to bash defaults: From cd4c323ffdae7d4ac91166cd76b1a9c419a0ac01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 16:19:24 +0100 Subject: [PATCH 14/15] readded token --- .github/workflows/platform.mirror-mar-file.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/platform.mirror-mar-file.yml b/.github/workflows/platform.mirror-mar-file.yml index 612f3eaf5..e3e59e44f 100644 --- a/.github/workflows/platform.mirror-mar-file.yml +++ b/.github/workflows/platform.mirror-mar-file.yml @@ -48,11 +48,11 @@ jobs: app-id: ${{ vars.MAR_REPO_ACCESS_APPID }} private-key: ${{ secrets.MAR_REPO_ACCESS_APP_PRIVATEKEY }} - # - uses: actions/create-github-app-token@v3 - # id: avm-app-token - # with: - # app-id: ${{ secrets.APP_ID }} - # private-key: ${{ secrets.APP_PRIVATE_KEY }} + - uses: actions/create-github-app-token@v3 + id: avm-app-token + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} # ensure the module is in the MAR file before publishing - name: Confirm module in MAR @@ -138,4 +138,4 @@ jobs: echo "Existing PR found: $CHECK_PULL_REQUEST_URL" fi env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.avm-app-token.outputs.token }} From fd28844a614968ea8ff22caf55d042462ce6542d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 25 Mar 2026 17:04:26 +0100 Subject: [PATCH 15/15] added names --- .github/workflows/platform.mirror-mar-file.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/platform.mirror-mar-file.yml b/.github/workflows/platform.mirror-mar-file.yml index e3e59e44f..bcb85d541 100644 --- a/.github/workflows/platform.mirror-mar-file.yml +++ b/.github/workflows/platform.mirror-mar-file.yml @@ -41,6 +41,7 @@ jobs: # create a token - uses: actions/create-github-app-token@v3 + name: Create App Token for MAR Repository id: mcr-app-token with: owner: ${{ vars.MAR_REPO_OWNER}} @@ -49,6 +50,7 @@ jobs: private-key: ${{ secrets.MAR_REPO_ACCESS_APP_PRIVATEKEY }} - uses: actions/create-github-app-token@v3 + name: Create App Token for AVM Repository (used for PR creation) id: avm-app-token with: app-id: ${{ secrets.APP_ID }}