diff --git a/.github/workflows/avm.res.sql.instance-pool.yml b/.github/workflows/avm.res.sql.instance-pool.yml index 75f6103864..659c39b82a 100644 --- a/.github/workflows/avm.res.sql.instance-pool.yml +++ b/.github/workflows/avm.res.sql.instance-pool.yml @@ -18,6 +18,7 @@ on: removeDeployment: type: boolean description: "Remove deployed module" + # We had to set this to false because the SQL Instance Pool takes more than 24 hours to get deleted required: false default: false push: diff --git a/.github/workflows/fork-on-push-brm-generate.yml b/.github/workflows/fork-on-push-brm-generate.yml deleted file mode 100644 index 06a832098c..0000000000 --- a/.github/workflows/fork-on-push-brm-generate.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: On push run brm generate # fork only, optional - -on: - push: - branches-ignore: - - main - paths: - - modules/**/*.bicep - - modules/**/*.sh - - modules/**/metadata.json - -jobs: - check-secret: - runs-on: ubuntu-latest - outputs: - my-key: ${{ steps.my-key.outputs.defined }} - steps: - - id: my-key - if: "${{ env.MY_KEY != '' }}" - run: echo "defined=true" >> $GITHUB_OUTPUT - env: - MY_KEY: ${{ secrets.PAT }} - - get-module-to-validate: - needs: [check-secret] - if: needs.check-secret.outputs.my-key == 'true' - uses: ./.github/workflows/get-changed-module.yml - - push-brm-generate: - runs-on: ubuntu-latest - needs: get-module-to-validate - if: needs.get-module-to-validate.outputs.module_dir - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.PAT }} # A PAT must be used to re-trigger workflows after commit. This PAT should NOT have the workflow scope. - - # Adding a step to explicitly install the latest Bicep CLI because there is - # always a delay in updating Bicep CLI in the job runner environments. - - name: Install the latest Bicep CLI - run: | - curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64 - chmod +x ./bicep - sudo mv ./bicep /usr/local/bin/bicep - bicep --version - - uses: jungwinter/split@master - id: branch - with: - msg: ${{ needs.get-module-to-validate.outputs.module_dir }} - separator: "/" - maxsplit: -1 - - - name: Bump version and push tag - id: tag_version - uses: mathieudutour/github-tag-action@v6.2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - tag_prefix: "${{ steps.branch.outputs._1 }}/${{ steps.branch.outputs._2 }}/" - dry_run: true - default_bump: false - - - name: Update Version - if: steps.tag_version.outputs.release_type - shell: bash - env: - OLD_VERSION: ${{ steps.tag_version.outputs.previous_version }} - VERSION: ${{ steps.tag_version.outputs.new_version }} - SAMPLEFOLDER_PATH: ${{ needs.get-module-to-validate.outputs.module_dir }} - MODULE: "${{ steps.branch.outputs._1 }}/${{ steps.branch.outputs._2 }}" - run: | - cd $SAMPLEFOLDER_PATH - NEW_VERSION=${VERSION%%-*} - # The following sed command searches for the specified module in README.md and replaces its version number - # (in formats X.Y.Z, X.Y, X, or a single lowercase letter) or adds a version number if it's not present. - REGEX=$(echo "s|($MODULE:)([0-9]+(\.[0-9]+){0,2})?|\1$NEW_VERSION|") - sed -ri "$REGEX" "README.md" || exit 1 - - - name: Run brm generate - env: - SAMPLEFOLDER_PATH: ${{ needs.get-module-to-validate.outputs.module_dir }} - run: | - dotnet tool install --global Azure.Bicep.RegistryModuleTool - - cd $SAMPLEFOLDER_PATH - brm generate - - - uses: EndBug/add-and-commit@v9 - with: - message: Autogenerate Bicep Files - committer_name: GitHub Actions - committer_email: actions@github.com - add: modules diff --git a/.github/workflows/fork-on-push-format-workflow.yml b/.github/workflows/fork-on-push-format-workflow.yml deleted file mode 100644 index 263a03088f..0000000000 --- a/.github/workflows/fork-on-push-format-workflow.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: On push format workflow files # fork only, optional - -on: - workflow_dispatch: - push: - branches-ignore: - - main - paths: - - .github/** - - CONTRIBUTING.md - -jobs: - check-secret: - runs-on: ubuntu-latest - outputs: - my-key: ${{ steps.my-key.outputs.defined }} - steps: - - id: my-key - if: "${{ env.MY_KEY != '' }}" - run: echo "defined=true" >> $GITHUB_OUTPUT - env: - MY_KEY: ${{ secrets.WORKFLOW_PAT }} - - push-prettier: - runs-on: ubuntu-latest - needs: [check-secret] - if: needs.check-secret.outputs.my-key == 'true' - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.WORKFLOW_PAT }} # A WORKFLOW_PAT must be used to re-trigger workflows after commit. This PAT must have the workflow scope. This is not generally recommended. - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "16" - - - name: Install packages - run: npm ci - - - name: Check formatting - run: npm run prettier --write . - - - uses: EndBug/add-and-commit@v9 - with: - message: Autogenerate Bicep Files - committer_name: GitHub Actions - committer_email: actions@github.com - add: .github CONTRIBUTING.md diff --git a/.github/workflows/get-changed-module.yml b/.github/workflows/get-changed-module.yml deleted file mode 100644 index 696faa3062..0000000000 --- a/.github/workflows/get-changed-module.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Get changed module - -on: - workflow_call: - outputs: - module_dir: - description: "The directory of the added or updated module. Empty if no module was changed." - value: ${{ jobs.main.outputs.module_dir }} - -jobs: - main: - runs-on: ubuntu-latest - outputs: - module_dir: ${{ steps.get-changed-module.outputs.result }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Get changed module - uses: actions/github-script@v7 - id: get-changed-module - with: - result-encoding: string - script: | - const script = require("./scripts/github-actions/get-changed-module.js") - return await script({ require, github, context, core }) diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml deleted file mode 100644 index 588b11f8ea..0000000000 --- a/.github/workflows/on-pull-request.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: On pull request - -on: - pull_request: - branches: - - main - -jobs: - get-module-to-validate: - uses: ./.github/workflows/get-changed-module.yml - - validate-module-files: - runs-on: ubuntu-latest - needs: get-module-to-validate - if: needs.get-module-to-validate.outputs.module_dir - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 7.0.x - - # Adding a step to explicitly install the latest Bicep CLI because there is - # always a delay in updating Bicep CLI in the job runner environments. - - name: Install the latest Bicep CLI - run: | - curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64 - chmod +x ./bicep - sudo mv ./bicep /usr/local/bin/bicep - bicep --version - - - name: Install Bicep registry module tool - run: dotnet tool install --global Azure.Bicep.RegistryModuleTool - - - name: Validate module files - run: brm validate - working-directory: ${{ needs.get-module-to-validate.outputs.module_dir }} - - - run: mv ${{ needs.get-module-to-validate.outputs.module_dir }}/main.json ${{ needs.get-module-to-validate.outputs.module_dir }}/mainTemplate.json - - - name: Run ARM-TTK (optional) - uses: microsoft/action-armttk@v1 - continue-on-error: true - with: - workdir: "${{ needs.get-module-to-validate.outputs.module_dir }}" - - shellcheck: - name: runner - runs-on: ubuntu-latest - needs: get-module-to-validate - if: needs.get-module-to-validate.outputs.module_dir - steps: - - uses: actions/checkout@v4 - - - name: shellcheck - uses: reviewdog/action-shellcheck@v1 - continue-on-error: true - with: - github_token: ${{ secrets.PAT || github.token }} - reporter: github-pr-review # Change reporter. - path: ${{ needs.get-module-to-validate.outputs.module_dir }} - - validate-non-module-files: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "16" - - - name: Install packages - run: npm ci - - - name: Lint - run: npm run lint - - - name: Check formatting - run: npm run prettier:check diff --git a/.github/workflows/on-push-main.yml b/.github/workflows/on-push-main.yml deleted file mode 100644 index 1e862b6a18..0000000000 --- a/.github/workflows/on-push-main.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: On push main - -on: - push: - branches: - - main - -jobs: - get-module-to-publish: - uses: ./.github/workflows/get-changed-module.yml - - create-tag: - runs-on: ubuntu-latest - needs: get-module-to-publish - if: ${{ needs.get-module-to-publish.outputs.module_dir }} - outputs: - tag: ${{ steps.create-tag.outputs.result }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install semver - run: npm install semver - - - name: Get base and head versions - id: get-versions - env: - PublicRelease: true - run: | - echo ::set-output name=base_version::$(nbgv get-version ${{ github.event.before }} --format json | jq '.SemVer2') - echo ::set-output name=head_version::$(nbgv get-version ${{ github.event.after }} --format json | jq '.SemVer2') - working-directory: ${{ needs.get-module-to-publish.outputs.module_dir }} - - - name: Create tag - id: create-tag - uses: actions/github-script@v7 - with: - result-encoding: string - script: | - const script = require("./scripts/github-actions/create-tag.js") - return await script({ - require, - github, - context, - core, - moduleDir: "${{ needs.get-module-to-publish.outputs.module_dir }}", - baseVersion: ${{ steps.get-versions.outputs.base_version }}, - headVersion: ${{ steps.get-versions.outputs.head_version }}, - }) - - publish-module: - needs: create-tag - if: needs.create-tag.outputs.tag - uses: ./.github/workflows/publish-module.yml - with: - tag: ${{ needs.create-tag.outputs.tag }} - secrets: - PUBLISH_CLIENT_ID: ${{ secrets.PUBLISH_CLIENT_ID }} - PUBLISH_TENANT_ID: ${{ secrets.PUBLISH_TENANT_ID }} - PUBLISH_SUBSCRIPTION_ID: ${{ secrets.PUBLISH_SUBSCRIPTION_ID }} - PUBLISH_REGISTRY_SERVER: ${{ secrets.PUBLISH_REGISTRY_SERVER }} diff --git a/.github/workflows/avm.platform.check.psrule.yml b/.github/workflows/platform.check.psrule.yml similarity index 98% rename from .github/workflows/avm.platform.check.psrule.yml rename to .github/workflows/platform.check.psrule.yml index 6a61e8bacd..c0a294a0a2 100644 --- a/.github/workflows/avm.platform.check.psrule.yml +++ b/.github/workflows/platform.check.psrule.yml @@ -1,4 +1,4 @@ -name: "avm.platform.check.psrule" +name: .Platform - Check PSRule on: workflow_dispatch: @@ -24,7 +24,7 @@ on: - cron: "0 12 * * 0" # Weekly Sunday Analysis env: - workflowPath: ".github/workflows/avm.platform.check.psrule.yml" + workflowPath: ".github/workflows/platform.check.psrule.yml" targetPath: "avm/res/" PSRuleOutputFilePath: "avm/res/PSRule-output.csv" PSRuleInputFilePath: "avm/res/PSRule-output.md" diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/platform.manage-workflow-issue.yml similarity index 100% rename from .github/workflows/avm.platform.manage-workflow-issue.yml rename to .github/workflows/platform.manage-workflow-issue.yml diff --git a/.github/workflows/on-pull-request-check-labels.yml b/.github/workflows/platform.on-pull-request-check-labels.yml similarity index 95% rename from .github/workflows/on-pull-request-check-labels.yml rename to .github/workflows/platform.on-pull-request-check-labels.yml index 011c0790e6..805c215f41 100644 --- a/.github/workflows/on-pull-request-check-labels.yml +++ b/.github/workflows/platform.on-pull-request-check-labels.yml @@ -1,4 +1,4 @@ -name: Check Labels +name: .Platform - Check Labels on: pull_request_target: diff --git a/.github/workflows/on-pull-request-check-pr-title.yml b/.github/workflows/platform.on-pull-request-check-pr-title.yml similarity index 88% rename from .github/workflows/on-pull-request-check-pr-title.yml rename to .github/workflows/platform.on-pull-request-check-pr-title.yml index a5e349f52c..ae9b65137f 100644 --- a/.github/workflows/on-pull-request-check-pr-title.yml +++ b/.github/workflows/platform.on-pull-request-check-pr-title.yml @@ -1,4 +1,4 @@ -name: "Semantic PR Check" +name: .Platform - Semantic PR Check on: pull_request_target: diff --git a/.github/workflows/avm.platform.publish-module-index-json.yml b/.github/workflows/platform.publish-module-index-json.yml similarity index 100% rename from .github/workflows/avm.platform.publish-module-index-json.yml rename to .github/workflows/platform.publish-module-index-json.yml diff --git a/.github/workflows/avm.platform.publish-tag.yml b/.github/workflows/platform.publish-tag.yml similarity index 99% rename from .github/workflows/avm.platform.publish-tag.yml rename to .github/workflows/platform.publish-tag.yml index 194531cad2..1bde3c4950 100644 --- a/.github/workflows/avm.platform.publish-tag.yml +++ b/.github/workflows/platform.publish-tag.yml @@ -1,4 +1,4 @@ -name: "avm.platform.publish-tag" +name: .Platform - Publish tag on: workflow_dispatch: diff --git a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml b/.github/workflows/platform.set-avm-github-issue-owner-config.yml similarity index 96% rename from .github/workflows/avm.platform.set-avm-github-issue-owner-config.yml rename to .github/workflows/platform.set-avm-github-issue-owner-config.yml index 84f3f7b138..306ced95b9 100644 --- a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml +++ b/.github/workflows/platform.set-avm-github-issue-owner-config.yml @@ -1,5 +1,5 @@ # Workflow for notifying and assigning issues on creation -name: .Platform - Set AVM GitHub issue owner config +name: .Platform - Set GitHub issue owner config on: issues: diff --git a/.github/workflows/avm.platform.sync-avm-modules-list.yml b/.github/workflows/platform.sync-avm-modules-list.yml similarity index 100% rename from .github/workflows/avm.platform.sync-avm-modules-list.yml rename to .github/workflows/platform.sync-avm-modules-list.yml diff --git a/.github/workflows/avm.platform.sync-repo-labels-from-csv.yml b/.github/workflows/platform.sync-repo-labels-from-csv.yml similarity index 95% rename from .github/workflows/avm.platform.sync-repo-labels-from-csv.yml rename to .github/workflows/platform.sync-repo-labels-from-csv.yml index d33fb79970..893a9ec3fc 100644 --- a/.github/workflows/avm.platform.sync-repo-labels-from-csv.yml +++ b/.github/workflows/platform.sync-repo-labels-from-csv.yml @@ -1,5 +1,5 @@ # Workflow for syncing CSV labels to GitHub Labels -name: avm.platform.sync-repo-labels-from-csv +name: .Platform - Sync repo labels from CSV on: schedule: diff --git a/.github/workflows/avm.platform.toggle-avm-workflows.yml b/.github/workflows/platform.toggle-avm-workflows.yml similarity index 97% rename from .github/workflows/avm.platform.toggle-avm-workflows.yml rename to .github/workflows/platform.toggle-avm-workflows.yml index 04381bea14..468f942a5b 100644 --- a/.github/workflows/avm.platform.toggle-avm-workflows.yml +++ b/.github/workflows/platform.toggle-avm-workflows.yml @@ -1,4 +1,4 @@ -name: "avm.platform.toggle-avm-workflows" +name: .Platform - Toggle AVM workflows on: workflow_dispatch: diff --git a/.github/workflows/publish-module.yml b/.github/workflows/publish-module.yml deleted file mode 100644 index d573607b20..0000000000 --- a/.github/workflows/publish-module.yml +++ /dev/null @@ -1,126 +0,0 @@ -name: Publish module - -on: - workflow_call: - inputs: - tag: - description: "The git tag of the module to publish." - required: true - type: string - secrets: - # Secrets must be passed from the caller workflow explicitly. - PUBLISH_CLIENT_ID: - required: true - PUBLISH_TENANT_ID: - required: true - PUBLISH_SUBSCRIPTION_ID: - required: true - PUBLISH_REGISTRY_SERVER: - required: true - workflow_dispatch: - inputs: - tag: - description: "The git tag of the module to publish." - required: true - type: string - -permissions: - id-token: write - contents: read - -jobs: - main: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Parse tag - id: parse-tag - uses: actions/github-script@v7 - with: - result-encoding: string - script: | - const script = require("./scripts/github-actions/parse-tag.js") - const tag = "${{ github.event.inputs.tag || inputs.tag }}" - script({ core, tag }) - - - name: Checkout tag - uses: actions/checkout@v4 - with: - # Input contexts for workflow_dispatch and workflow_call are inconsistent. - # For workflow_dispatch, use ${{ github.event.inputs. }} - # For workflow_call, use ${{ inputs. }} - ref: ${{ github.event.inputs.tag || inputs.tag }} - - - name: Log in to Azure - uses: azure/login@v2 - with: - client-id: ${{ secrets.PUBLISH_CLIENT_ID }} - tenant-id: ${{ secrets.PUBLISH_TENANT_ID }} - subscription-id: ${{ secrets.PUBLISH_SUBSCRIPTION_ID }} - - # Adding a step to explicitly install the latest Bicep CLI because there is - # always a delay in updating Bicep CLI in the job runner environments. - - name: Install the latest Bicep CLI - run: | - curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64 - chmod +x ./bicep - sudo mv ./bicep /usr/local/bin/bicep - bicep --version - - - name: Publish module - run: bicep publish "modules/${{ steps.parse-tag.outputs.module_path }}/main.bicep" --target "br:${{ secrets.PUBLISH_REGISTRY_SERVER }}/public/bicep/${{ steps.parse-tag.outputs.module_path }}:${{ steps.parse-tag.outputs.version }}" --documentationUri "${{ steps.parse-tag.outputs.documentation_uri }}" --with-source --force - - - name: Validate publish - run: | - Version="${{ steps.parse-tag.outputs.version }}" - ModulePath="${{ steps.parse-tag.outputs.module_path }}" - - time_limit_seconds=3600 - end_time=$((SECONDS+time_limit_seconds)) - retry_seconds=5 - - while true; do - CATALOG=https://mcr.microsoft.com/v2/_catalog - echo curl -sLo catalog $CATALOG - curl -sLo catalog $CATALOG - cat catalog | fgrep "bicep/" > bicepcatalog - echo "Bicep modules found in MCR catalog:" - cat bicepcatalog - - if fgrep -q "\"bicep/$ModulePath\"" catalog; then - echo "Passed: Found module $ModulePath in the MCR catalog" - break - else - echo "Error: Module $ModulePath is not in the MCR catalog. Retrying in $retry_seconds seconds" - sleep $retry_seconds - fi - - if [ $SECONDS -ge $end_time ]; then - echo "Time limit reached. Failed to validate publish within the specified time." - exit 1 - fi - done - - while true; do - TAGS=https://mcr.microsoft.com/v2/bicep/$ModulePath/tags/list - echo curl -sLo tags $TAGS - curl -sLo tags $TAGS - echo "Tags:" - cat tags - - echo - if fgrep -q "$Version" tags; then - echo "Passed: Found new tag $Version for published module" - break - else - echo "Error: Coud not find new tag $Version for published module. Retrying in $retry_seconds seconds" - sleep $retry_seconds - fi - - if [ $SECONDS -ge $end_time ]; then - echo "Time limit reached. Failed to validate publish within the specified time." - exit 1 - fi - done diff --git a/avm/res/compute/virtual-machine/README.md b/avm/res/compute/virtual-machine/README.md index 9907ba0feb..4c3e5631ad 100644 --- a/avm/res/compute/virtual-machine/README.md +++ b/avm/res/compute/virtual-machine/README.md @@ -18,13 +18,14 @@ This module deploys a Virtual Machine with one or multiple NICs and optionally o | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Automanage/configurationProfileAssignments` | [2021-04-30-preview](https://learn.microsoft.com/en-us/azure/templates) | -| `Microsoft.Compute/virtualMachines` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-11-01/virtualMachines) | +| `Microsoft.Automanage/configurationProfileAssignments` | [2022-05-04](https://learn.microsoft.com/en-us/azure/templates) | +| `Microsoft.Compute/virtualMachines` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-09-01/virtualMachines) | | `Microsoft.Compute/virtualMachines/extensions` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-11-01/virtualMachines/extensions) | +| `Microsoft.DevTestLab/schedules` | [2018-09-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DevTestLab/2018-09-15/schedules) | | `Microsoft.GuestConfiguration/guestConfigurationAssignments` | [2020-06-25](https://learn.microsoft.com/en-us/azure/templates/Microsoft.GuestConfiguration/2020-06-25/guestConfigurationAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | -| `Microsoft.Network/publicIPAddresses` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/publicIPAddresses) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/publicIPAddresses) | | `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | ## Usage examples @@ -61,7 +62,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdminUser' - availabilityZone: 0 imageReference: { offer: '0001-com-ubuntu-server-jammy' publisher: 'Canonical' @@ -76,13 +76,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { name: 'ipconfig01' pipConfiguration: { publicIpNameSuffix: '-pip-01' + zones: [ + 1 + 2 + 3 + ] } subnetResourceId: '' - zones: [ - '1' - '2' - '3' - ] } ] nicSuffix: '-nic-01' @@ -96,6 +96,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Linux' vmSize: 'Standard_DS2_v2' + zone: 0 // Non-required parameters configurationProfile: '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' disablePasswordAuthentication: true @@ -126,9 +127,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdminUser" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "0001-com-ubuntu-server-jammy", @@ -147,14 +145,14 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { { "name": "ipconfig01", "pipConfiguration": { - "publicIpNameSuffix": "-pip-01" + "publicIpNameSuffix": "-pip-01", + "zones": [ + 1, + 2, + 3 + ] }, - "subnetResourceId": "", - "zones": [ - "1", - "2", - "3" - ] + "subnetResourceId": "" } ], "nicSuffix": "-nic-01" @@ -175,6 +173,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 0 + }, // Non-required parameters "configurationProfile": { "value": "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction" @@ -215,7 +216,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdminUser' - availabilityZone: 0 imageReference: { offer: '0001-com-ubuntu-server-jammy' publisher: 'Canonical' @@ -246,6 +246,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Linux' vmSize: 'Standard_DS2_v2' + zone: 0 // Non-required parameters disablePasswordAuthentication: true location: '' @@ -275,9 +276,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdminUser" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "0001-com-ubuntu-server-jammy", @@ -320,6 +318,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 0 + }, // Non-required parameters "disablePasswordAuthentication": { "value": true @@ -357,7 +358,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdministrator' - availabilityZone: 1 imageReference: { offer: '0001-com-ubuntu-server-focal' publisher: 'Canonical' @@ -418,16 +418,16 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { roleDefinitionIdOrName: 'Reader' } ] + zones: [ + 1 + 2 + 3 + ] } subnetResourceId: '' - zones: [ - '1' - '2' - '3' - ] } ] - nicSuffix: '-nic-01' + name: 'nic-test-01' roleAssignments: [ { principalId: '' @@ -449,6 +449,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Linux' vmSize: 'Standard_DS2_v2' + zone: 1 // Non-required parameters backupPolicyName: '' backupVaultName: '' @@ -523,6 +524,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { commandToExecute: '' } extensionDependencyAgentConfig: { + enableAMA: true enabled: true tags: { Environment: 'Non-Prod' @@ -573,6 +575,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { path: '/home/localAdministrator/.ssh/authorized_keys' } ] + rebootSetting: 'IfRequired' roleAssignments: [ { principalId: '' @@ -615,9 +618,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdministrator" }, - "availabilityZone": { - "value": 1 - }, "imageReference": { "value": { "offer": "0001-com-ubuntu-server-focal", @@ -682,17 +682,17 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Reader" } + ], + "zones": [ + 1, + 2, + 3 ] }, - "subnetResourceId": "", - "zones": [ - "1", - "2", - "3" - ] + "subnetResourceId": "" } ], - "nicSuffix": "-nic-01", + "name": "nic-test-01", "roleAssignments": [ { "principalId": "", @@ -721,6 +721,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 1 + }, // Non-required parameters "backupPolicyName": { "value": "" @@ -820,6 +823,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionDependencyAgentConfig": { "value": { + "enableAMA": true, "enabled": true, "tags": { "Environment": "Non-Prod", @@ -887,6 +891,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } ] }, + "rebootSetting": { + "value": "IfRequired" + }, "roleAssignments": { "value": [ { @@ -935,7 +942,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'VMAdmin' - availabilityZone: 2 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' @@ -996,13 +1002,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { roleDefinitionIdOrName: 'Reader' } ] + zones: [ + 1 + 2 + 3 + ] } subnetResourceId: '' - zones: [ - '1' - '2' - '3' - ] } ] nicSuffix: '-nic-01' @@ -1026,6 +1032,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_DS2_v2' + zone: 2 // Non-required parameters adminPassword: '' backupPolicyName: '' @@ -1121,6 +1128,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { commandToExecute: '' } extensionDependencyAgentConfig: { + enableAMA: true enabled: true tags: { Environment: 'Non-Prod' @@ -1207,9 +1215,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "VMAdmin" }, - "availabilityZone": { - "value": 2 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -1274,14 +1279,14 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Reader" } + ], + "zones": [ + 1, + 2, + 3 ] }, - "subnetResourceId": "", - "zones": [ - "1", - "2", - "3" - ] + "subnetResourceId": "" } ], "nicSuffix": "-nic-01", @@ -1312,6 +1317,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 2 + }, // Non-required parameters "adminPassword": { "value": "" @@ -1434,6 +1442,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionDependencyAgentConfig": { "value": { + "enableAMA": true, "enabled": true, "tags": { "Environment": "Non-Prod", @@ -1543,7 +1552,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdminUser' - availabilityZone: 0 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' @@ -1571,6 +1579,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_DS2_v2' + zone: 0 // Non-required parameters adminPassword: '' location: '' @@ -1594,9 +1603,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdminUser" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -1636,6 +1642,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 0 + }, // Non-required parameters "adminPassword": { "value": "" @@ -1665,7 +1674,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdminUser' - availabilityZone: 0 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' @@ -1693,6 +1701,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_DS2_v2' + zone: 0 // Non-required parameters adminPassword: '' extensionGuestConfigurationExtension: { @@ -1745,9 +1754,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdminUser" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -1787,6 +1793,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 0 + }, // Non-required parameters "adminPassword": { "value": "" @@ -1851,7 +1860,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdminUser' - availabilityZone: 0 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' @@ -1879,6 +1887,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_DS2_v2' + zone: 0 // Non-required parameters adminPassword: '' extensionAadJoinConfig: { @@ -1925,9 +1934,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdminUser" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -1967,6 +1973,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 0 + }, // Non-required parameters "adminPassword": { "value": "" @@ -2025,7 +2034,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'VMAdmin' - availabilityZone: 2 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' @@ -2050,6 +2058,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { workspaceResourceId: '' } ] + enableIPForwarding: true ipConfigurations: [ { applicationSecurityGroups: [ @@ -2086,16 +2095,16 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { roleDefinitionIdOrName: 'Reader' } ] + zones: [ + 1 + 2 + 3 + ] } subnetResourceId: '' - zones: [ - '1' - '2' - '3' - ] } ] - nicSuffix: '-nic-01' + name: 'nic-test-01' roleAssignments: [ { principalId: '' @@ -2117,8 +2126,18 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_DS2_v2' + zone: 2 // Non-required parameters adminPassword: '' + autoShutdownConfig: { + notificationEmail: 'test@contoso.com' + notificationLocale: 'en' + notificationStatus: 'Enabled' + notificationTimeInMinutes: 30 + status: 'Enabled' + time: '19:00' + timeZone: 'UTC' + } backupPolicyName: '' backupVaultName: '' backupVaultResourceGroup: '' @@ -2216,6 +2235,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { commandToExecute: '' } extensionDependencyAgentConfig: { + enableAMA: true enabled: true tags: { Environment: 'Non-Prod' @@ -2261,6 +2281,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } patchMode: 'AutomaticByPlatform' proximityPlacementGroupResourceId: '' + rebootSetting: 'IfRequired' roleAssignments: [ { principalId: '' @@ -2303,9 +2324,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "VMAdmin" }, - "availabilityZone": { - "value": 2 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -2335,6 +2353,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "workspaceResourceId": "" } ], + "enableIPForwarding": true, "ipConfigurations": [ { "applicationSecurityGroups": [ @@ -2370,17 +2389,17 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "principalType": "ServicePrincipal", "roleDefinitionIdOrName": "Reader" } + ], + "zones": [ + 1, + 2, + 3 ] }, - "subnetResourceId": "", - "zones": [ - "1", - "2", - "3" - ] + "subnetResourceId": "" } ], - "nicSuffix": "-nic-01", + "name": "nic-test-01", "roleAssignments": [ { "principalId": "", @@ -2409,10 +2428,24 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 2 + }, // Non-required parameters "adminPassword": { "value": "" }, + "autoShutdownConfig": { + "value": { + "notificationEmail": "test@contoso.com", + "notificationLocale": "en", + "notificationStatus": "Enabled", + "notificationTimeInMinutes": 30, + "status": "Enabled", + "time": "19:00", + "timeZone": "UTC" + } + }, "backupPolicyName": { "value": "" }, @@ -2535,6 +2568,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "extensionDependencyAgentConfig": { "value": { + "enableAMA": true, "enabled": true, "tags": { "Environment": "Non-Prod", @@ -2597,6 +2631,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "proximityPlacementGroupResourceId": { "value": "" }, + "rebootSetting": { + "value": "IfRequired" + }, "roleAssignments": { "value": [ { @@ -2645,14 +2682,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'localAdminUser' - availabilityZone: 0 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' sku: '2022-datacenter-azure-edition' version: 'latest' } - name: 'cvmwinnvidia' + name: 'cvmwinnv' nicConfigurations: [ { ipConfigurations: [ @@ -2673,6 +2709,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_NV6ads_A10_v5' + zone: 0 // Non-required parameters adminPassword: '' extensionNvidiaGpuDriverWindows: { @@ -2699,9 +2736,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "localAdminUser" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -2711,7 +2745,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } }, "name": { - "value": "cvmwinnvidia" + "value": "cvmwinnv" }, "nicConfigurations": { "value": [ @@ -2741,6 +2775,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_NV6ads_A10_v5" }, + "zone": { + "value": 0 + }, // Non-required parameters "adminPassword": { "value": "" @@ -2775,7 +2812,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { params: { // Required parameters adminUsername: 'VMAdministrator' - availabilityZone: 0 imageReference: { offer: 'WindowsServer' publisher: 'MicrosoftWindowsServer' @@ -2805,6 +2841,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } osType: 'Windows' vmSize: 'Standard_DS2_v2' + zone: 0 // Non-required parameters adminPassword: '' dataDisks: [ @@ -2839,9 +2876,6 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "adminUsername": { "value": "VMAdministrator" }, - "availabilityZone": { - "value": 0 - }, "imageReference": { "value": { "offer": "WindowsServer", @@ -2883,6 +2917,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "vmSize": { "value": "Standard_DS2_v2" }, + "zone": { + "value": 0 + }, // Non-required parameters "adminPassword": { "value": "" @@ -2918,14 +2955,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`adminUsername`](#parameter-adminusername) | securestring | Administrator username. | -| [`availabilityZone`](#parameter-availabilityzone) | int | If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set. | -| [`configurationProfile`](#parameter-configurationprofile) | string | The configuration profile of automanage. | | [`imageReference`](#parameter-imagereference) | object | OS image reference. In case of marketplace images, it's the combination of the publisher, offer, sku, version attributes. In case of custom images it's the resource ID of the custom image. | | [`name`](#parameter-name) | string | The name of the virtual machine to be created. You should use a unique prefix to reduce name collisions in Active Directory. | | [`nicConfigurations`](#parameter-nicconfigurations) | array | Configures NICs and PIPs. | | [`osDisk`](#parameter-osdisk) | object | Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. | | [`osType`](#parameter-ostype) | string | The chosen OS type. | | [`vmSize`](#parameter-vmsize) | string | Specifies the size for the VMs. | +| [`zone`](#parameter-zone) | int | If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set. | **Optional parameters** @@ -2934,6 +2970,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`additionalUnattendContent`](#parameter-additionalunattendcontent) | array | Specifies additional XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. Contents are defined by setting name, component name, and the pass in which the content is applied. | | [`adminPassword`](#parameter-adminpassword) | securestring | When specifying a Windows Virtual Machine, this value should be passed. | | [`allowExtensionOperations`](#parameter-allowextensionoperations) | bool | Specifies whether extension operations should be allowed on the virtual machine. This may only be set to False when no extensions are present on the virtual machine. | +| [`autoShutdownConfig`](#parameter-autoshutdownconfig) | object | The configuration for auto-shutdown. | | [`availabilitySetResourceId`](#parameter-availabilitysetresourceid) | string | Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set. | | [`backupPolicyName`](#parameter-backuppolicyname) | string | Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault. | | [`backupVaultName`](#parameter-backupvaultname) | string | Recovery service vault name to add VMs to backup. | @@ -2941,8 +2978,10 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`bootDiagnostics`](#parameter-bootdiagnostics) | bool | Whether boot diagnostics should be enabled on the Virtual Machine. Boot diagnostics will be enabled with a managed storage account if no bootDiagnosticsStorageAccountName value is provided. If bootDiagnostics and bootDiagnosticsStorageAccountName values are not provided, boot diagnostics will be disabled. | | [`bootDiagnosticStorageAccountName`](#parameter-bootdiagnosticstorageaccountname) | string | Custom storage account used to store boot diagnostic information. Boot diagnostics will be enabled with a custom storage account if a value is provided. | | [`bootDiagnosticStorageAccountUri`](#parameter-bootdiagnosticstorageaccounturi) | string | Storage account boot diagnostic base URI. | +| [`bypassPlatformSafetyChecksOnUserSchedule`](#parameter-bypassplatformsafetychecksonuserschedule) | bool | Enables customer to schedule patching without accidental upgrades. | | [`certificatesToBeInstalled`](#parameter-certificatestobeinstalled) | array | Specifies set of certificates that should be installed onto the virtual machine. | | [`computerName`](#parameter-computername) | string | Can be used if the computer name needs to be different from the Azure VM resource name. If not used, the resource name will be used as computer name. | +| [`configurationProfile`](#parameter-configurationprofile) | string | The configuration profile of automanage. Either '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction', 'providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' or the resource Id of custom profile. | | [`customData`](#parameter-customdata) | string | Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. | | [`dataDisks`](#parameter-datadisks) | array | Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs. | | [`dedicatedHostId`](#parameter-dedicatedhostid) | string | Specifies resource ID about the dedicated host that the virtual machine resides in. | @@ -2967,7 +3006,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`extensionNetworkWatcherAgentConfig`](#parameter-extensionnetworkwatcheragentconfig) | object | The configuration for the [Network Watcher Agent] extension. Must at least contain the ["enabled": true] property to be executed. | | [`extensionNvidiaGpuDriverWindows`](#parameter-extensionnvidiagpudriverwindows) | object | The configuration for the [Nvidia Gpu Driver Windows] extension. Must at least contain the ["enabled": true] property to be executed. | | [`guestConfiguration`](#parameter-guestconfiguration) | object | The guest configuration for the virtual machine. Needs the Guest Configuration extension to be enabled. | -| [`licenseType`](#parameter-licensetype) | string | Specifies that the image or disk that is being used was licensed on-premises. This element is only used for images that contain the Windows Server operating system. | +| [`licenseType`](#parameter-licensetype) | string | Specifies that the image or disk that is being used was licensed on-premises. | | [`location`](#parameter-location) | string | Location for all resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. The system-assigned managed identity will automatically be enabled if extensionAadJoinConfig.enabled = "True". | @@ -2979,6 +3018,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { | [`provisionVMAgent`](#parameter-provisionvmagent) | bool | Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later. | | [`proximityPlacementGroupResourceId`](#parameter-proximityplacementgroupresourceid) | string | Resource ID of a proximity placement group. | | [`publicKeys`](#parameter-publickeys) | array | The list of SSH public keys used to authenticate with linux based VMs. | +| [`rebootSetting`](#parameter-rebootsetting) | string | Specifies the reboot setting for all AutomaticByPlatform patch installation operations. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`sasTokenValidityLength`](#parameter-sastokenvaliditylength) | string | SAS token validity length to use to download files from storage accounts. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours. | | [`secureBootEnabled`](#parameter-securebootenabled) | bool | Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings. | @@ -3002,38 +3042,6 @@ Administrator username. - Required: Yes - Type: securestring -### Parameter: `availabilityZone` - -If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set. - -- Required: Yes -- Type: int -- Allowed: - ```Bicep - [ - 0 - 1 - 2 - 3 - ] - ``` - -### Parameter: `configurationProfile` - -The configuration profile of automanage. - -- Required: No -- Type: string -- Default: `''` -- Allowed: - ```Bicep - [ - '' - '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' - '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' - ] - ``` - ### Parameter: `imageReference` OS image reference. In case of marketplace images, it's the combination of the publisher, offer, sku, version attributes. In case of custom images it's the resource ID of the custom image. @@ -3202,6 +3210,22 @@ Specifies the size for the VMs. - Required: Yes - Type: string +### Parameter: `zone` + +If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set. + +- Required: Yes +- Type: int +- Allowed: + ```Bicep + [ + 0 + 1 + 2 + 3 + ] + ``` + ### Parameter: `additionalUnattendContent` Specifies additional XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. Contents are defined by setting name, component name, and the pass in which the content is applied. @@ -3226,6 +3250,14 @@ Specifies whether extension operations should be allowed on the virtual machine. - Type: bool - Default: `True` +### Parameter: `autoShutdownConfig` + +The configuration for auto-shutdown. + +- Required: No +- Type: object +- Default: `{}` + ### Parameter: `availabilitySetResourceId` Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set. @@ -3282,6 +3314,14 @@ Storage account boot diagnostic base URI. - Type: string - Default: `[format('.blob.{0}/', environment().suffixes.storage)]` +### Parameter: `bypassPlatformSafetyChecksOnUserSchedule` + +Enables customer to schedule patching without accidental upgrades. + +- Required: No +- Type: bool +- Default: `True` + ### Parameter: `certificatesToBeInstalled` Specifies set of certificates that should be installed onto the virtual machine. @@ -3298,6 +3338,14 @@ Can be used if the computer name needs to be different from the Azure VM resourc - Type: string - Default: `[parameters('name')]` +### Parameter: `configurationProfile` + +The configuration profile of automanage. Either '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction', 'providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' or the resource Id of custom profile. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `customData` Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format. @@ -3679,7 +3727,7 @@ The guest configuration for the virtual machine. Needs the Guest Configuration e ### Parameter: `licenseType` -Specifies that the image or disk that is being used was licensed on-premises. This element is only used for images that contain the Windows Server operating system. +Specifies that the image or disk that is being used was licensed on-premises. - Required: No - Type: string @@ -3854,6 +3902,23 @@ The list of SSH public keys used to authenticate with linux based VMs. - Type: array - Default: `[]` +### Parameter: `rebootSetting` + +Specifies the reboot setting for all AutomaticByPlatform patch installation operations. + +- Required: No +- Type: string +- Default: `'IfRequired'` +- Allowed: + ```Bicep + [ + 'Always' + 'IfRequired' + 'Never' + 'Unknown' + ] + ``` + ### Parameter: `roleAssignments` Array of role assignments to create. @@ -4031,8 +4096,8 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/network/network-interface:0.2.2` | Remote reference | -| `br/public:avm/res/network/public-ip-address:0.2.1` | Remote reference | +| `br/public:avm/res/network/network-interface:0.2.4` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.4.0` | Remote reference | ## Notes diff --git a/avm/res/compute/virtual-machine/main.bicep b/avm/res/compute/virtual-machine/main.bicep index 4d5e48280a..e719183020 100644 --- a/avm/res/compute/virtual-machine/main.bicep +++ b/avm/res/compute/virtual-machine/main.bicep @@ -69,7 +69,7 @@ param maxPriceForLowPriorityVm string = '' @description('Optional. Specifies resource ID about the dedicated host that the virtual machine resides in.') param dedicatedHostId string = '' -@description('Optional. Specifies that the image or disk that is being used was licensed on-premises. This element is only used for images that contain the Windows Server operating system.') +@description('Optional. Specifies that the image or disk that is being used was licensed on-premises.') @allowed([ 'Windows_Client' 'Windows_Server' @@ -105,7 +105,7 @@ param availabilitySetResourceId string = '' 2 3 ]) -param availabilityZone int +param zone int // External resources @description('Required. Configures NICs and PIPs.') @@ -120,6 +120,9 @@ param backupVaultResourceGroup string = resourceGroup().name @description('Optional. Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault.') param backupPolicyName string = 'DefaultPolicy' +@description('Optional. The configuration for auto-shutdown.') +param autoShutdownConfig object = {} + // Child resources @description('Optional. Specifies whether extension operations should be allowed on the virtual machine. This may only be set to False when no extensions are present on the virtual machine.') param allowExtensionOperations bool = true @@ -249,6 +252,18 @@ param enableAutomaticUpdates bool = true ]) param patchMode string = '' +@description('Optional. Enables customer to schedule patching without accidental upgrades.') +param bypassPlatformSafetyChecksOnUserSchedule bool = true + +@description('Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations.') +@allowed([ + 'Always' + 'IfRequired' + 'Never' + 'Unknown' +]) +param rebootSetting string = 'IfRequired' + @description('Optional. VM guest patching assessment mode. Set it to \'AutomaticByPlatform\' to enable automatically check for updates every 24 hours.') @allowed([ 'AutomaticByPlatform' @@ -265,12 +280,7 @@ param additionalUnattendContent array = [] @description('Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell. - WinRMConfiguration object.') param winRM array = [] -@description('Required. The configuration profile of automanage.') -@allowed([ - '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction' - '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' - '' -]) +@description('Optional. The configuration profile of automanage. Either \'/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction\', \'providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest\' or the resource Id of custom profile.') param configurationProfile string = '' var publicKeysFormatted = [ @@ -290,6 +300,10 @@ var linuxConfiguration = { ? { patchMode: patchMode assessmentMode: patchAssessmentMode + automaticByPlatformSettings: { + bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule + rebootSetting: rebootSetting + } } : null } @@ -301,6 +315,10 @@ var windowsConfiguration = { ? { patchMode: patchMode assessmentMode: patchAssessmentMode + automaticByPlatformSettings: { + bypassPlatformSafetyChecksOnUserSchedule: bypassPlatformSafetyChecksOnUserSchedule + rebootSetting: rebootSetting + } } : null timeZone: empty(timeZone) ? null : timeZone @@ -425,12 +443,12 @@ module vm_nic 'modules/nic-configuration.bicep' = [ for (nicConfiguration, index) in nicConfigurations: { name: '${uniqueString(deployment().name, location)}-VM-Nic-${index}' params: { - networkInterfaceName: '${name}${nicConfiguration.nicSuffix}' + networkInterfaceName: contains(nicConfiguration, 'name') + ? nicConfiguration.name + : '${name}${nicConfiguration.nicSuffix}' virtualMachineName: name location: location - enableIPForwarding: contains(nicConfiguration, 'enableIPForwarding') - ? (!empty(nicConfiguration.enableIPForwarding) ? nicConfiguration.enableIPForwarding : false) - : false + enableIPForwarding: contains(nicConfiguration, 'enableIPForwarding') ? nicConfiguration.enableIPForwarding : false enableAcceleratedNetworking: contains(nicConfiguration, 'enableAcceleratedNetworking') ? nicConfiguration.enableAcceleratedNetworking : true @@ -450,12 +468,12 @@ module vm_nic 'modules/nic-configuration.bicep' = [ } ] -resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = { +resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = { name: name location: location identity: identity tags: tags - zones: availabilityZone != 0 ? array(string(availabilityZone)) : null + zones: zone != 0 ? array(string(zone)) : null plan: !empty(plan) ? plan : null properties: { hardwareProfile: { @@ -522,7 +540,10 @@ resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = { primary: index == 0 ? true : false } #disable-next-line use-resource-id-functions // It's a reference from inside a loop which makes resolving it using a resource reference particulary difficult. - id: az.resourceId('Microsoft.Network/networkInterfaces', '${name}${nicConfiguration.nicSuffix}') + id: az.resourceId( + 'Microsoft.Network/networkInterfaces', + contains(nicConfiguration, 'name') ? nicConfiguration.name : '${name}${nicConfiguration.nicSuffix}' + ) } ] } @@ -564,7 +585,7 @@ resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = { ] } -resource vm_configurationProfileAssignment 'Microsoft.Automanage/configurationProfileAssignments@2021-04-30-preview' = +resource vm_configurationProfileAssignment 'Microsoft.Automanage/configurationProfileAssignments@2022-05-04' = if (!empty(configurationProfile)) { name: 'default' properties: { @@ -573,6 +594,40 @@ resource vm_configurationProfileAssignment 'Microsoft.Automanage/configurationPr scope: vm } +resource vm_autoShutdownConfiguration 'Microsoft.DevTestLab/schedules@2018-09-15' = + if (!empty(autoShutdownConfig)) { + name: 'shutdown-computevm-${vm.name}' + location: location + properties: { + status: contains(autoShutdownConfig, 'status') ? autoShutdownConfig.status : 'Disabled' + targetResourceId: vm.id + taskType: 'ComputeVmShutdownTask' + dailyRecurrence: { + time: contains(autoShutdownConfig, 'time') ? autoShutdownConfig.time : '19:00' + } + timeZoneId: contains(autoShutdownConfig, 'timeZone') ? autoShutdownConfig.timeZone : 'UTC' + notificationSettings: contains(autoShutdownConfig, 'notificationStatus') + ? { + status: contains(autoShutdownConfig, 'notificationStatus') + ? autoShutdownConfig.notificationStatus + : 'Disabled' + emailRecipient: contains(autoShutdownConfig, 'notificationEmail') + ? autoShutdownConfig.notificationEmail + : '' + notificationLocale: contains(autoShutdownConfig, 'notificationLocale') + ? autoShutdownConfig.notificationLocale + : 'en' + timeInMinutes: contains(autoShutdownConfig, 'notificationWebhookUrl') + ? autoShutdownConfig.notificationWebhookUrl + : '' + webhookUrl: contains(autoShutdownConfig, 'notificationTimeInMinutes') + ? autoShutdownConfig.notificationTimeInMinutes + : 30 + } + : null + } + } + module vm_aadJoinExtension 'extension/main.bicep' = if (extensionAadJoinConfig.enabled) { name: '${uniqueString(deployment().name, location)}-VM-AADLogin' @@ -716,13 +771,18 @@ module vm_dependencyAgentExtension 'extension/main.bicep' = type: osType == 'Windows' ? 'DependencyAgentWindows' : 'DependencyAgentLinux' typeHandlerVersion: contains(extensionDependencyAgentConfig, 'typeHandlerVersion') ? extensionDependencyAgentConfig.typeHandlerVersion - : '9.5' + : '9.10' autoUpgradeMinorVersion: contains(extensionDependencyAgentConfig, 'autoUpgradeMinorVersion') ? extensionDependencyAgentConfig.autoUpgradeMinorVersion : true enableAutomaticUpgrade: contains(extensionDependencyAgentConfig, 'enableAutomaticUpgrade') ? extensionDependencyAgentConfig.enableAutomaticUpgrade : true + settings: { + enableAMA: contains(extensionDependencyAgentConfig, 'enableAMA') + ? extensionDependencyAgentConfig.enableAMA + : true + } tags: extensionDependencyAgentConfig.?tags ?? tags } dependsOn: [ diff --git a/avm/res/compute/virtual-machine/main.json b/avm/res/compute/virtual-machine/main.json index 0d19a77932..1f05529bc1 100644 --- a/avm/res/compute/virtual-machine/main.json +++ b/avm/res/compute/virtual-machine/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15754009504409627537" + "version": "0.26.54.24096", + "templateHash": "13812487090890990585" }, "name": "Virtual Machines", "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", @@ -456,7 +456,7 @@ "" ], "metadata": { - "description": "Optional. Specifies that the image or disk that is being used was licensed on-premises. This element is only used for images that contain the Windows Server operating system." + "description": "Optional. Specifies that the image or disk that is being used was licensed on-premises." } }, "publicKeys": { @@ -507,7 +507,7 @@ "description": "Optional. Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set." } }, - "availabilityZone": { + "zone": { "type": "int", "allowedValues": [ 0, @@ -546,6 +546,13 @@ "description": "Optional. Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault." } }, + "autoShutdownConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for auto-shutdown." + } + }, "allowExtensionOperations": { "type": "bool", "defaultValue": true, @@ -782,6 +789,26 @@ "description": "Optional. VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'." } }, + "bypassPlatformSafetyChecksOnUserSchedule": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enables customer to schedule patching without accidental upgrades." + } + }, + "rebootSetting": { + "type": "string", + "defaultValue": "IfRequired", + "allowedValues": [ + "Always", + "IfRequired", + "Never", + "Unknown" + ], + "metadata": { + "description": "Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations." + } + }, "patchAssessmentMode": { "type": "string", "defaultValue": "ImageDefault", @@ -817,13 +844,8 @@ "configurationProfile": { "type": "string", "defaultValue": "", - "allowedValues": [ - "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction", - "/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest", - "" - ], "metadata": { - "description": "Required. The configuration profile of automanage." + "description": "Optional. The configuration profile of automanage. Either '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction', 'providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' or the resource Id of custom profile." } } }, @@ -844,12 +866,12 @@ "publicKeys": "[variables('publicKeysFormatted')]" }, "provisionVMAgent": "[parameters('provisionVMAgent')]", - "patchSettings": "[if(and(parameters('provisionVMAgent'), or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('ImageDefault')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode')), null())]" + "patchSettings": "[if(and(parameters('provisionVMAgent'), or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('ImageDefault')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'automaticByPlatformSettings', createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting'))), null())]" }, "windowsConfiguration": { "provisionVMAgent": "[parameters('provisionVMAgent')]", "enableAutomaticUpdates": "[parameters('enableAutomaticUpdates')]", - "patchSettings": "[if(and(parameters('provisionVMAgent'), or(or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('AutomaticByOS'))), equals(toLower(parameters('patchMode')), toLower('Manual')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode')), null())]", + "patchSettings": "[if(and(parameters('provisionVMAgent'), or(or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('AutomaticByOS'))), equals(toLower(parameters('patchMode')), toLower('Manual')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'automaticByPlatformSettings', createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting'))), null())]", "timeZone": "[if(empty(parameters('timeZone')), null(), parameters('timeZone'))]", "additionalUnattendContent": "[if(empty(parameters('additionalUnattendContent')), null(), parameters('additionalUnattendContent'))]", "winRM": "[if(not(empty(parameters('winRM'))), createObject('listeners', parameters('winRM')), null())]" @@ -907,12 +929,12 @@ }, "vm": { "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2022-11-01", + "apiVersion": "2023-09-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "identity": "[variables('identity')]", "tags": "[parameters('tags')]", - "zones": "[if(not(equals(parameters('availabilityZone'), 0)), array(string(parameters('availabilityZone'))), null())]", + "zones": "[if(not(equals(parameters('zone'), 0)), array(string(parameters('zone'))), null())]", "plan": "[if(not(empty(parameters('plan'))), parameters('plan'), null())]", "properties": { "hardwareProfile": { @@ -980,7 +1002,7 @@ "deleteOption": "[if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'deleteOption'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].deleteOption, 'Delete')]", "primary": "[if(equals(copyIndex('networkInterfaces'), 0), true(), false())]" }, - "id": "[resourceId('Microsoft.Network/networkInterfaces', format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].nicSuffix))]" + "id": "[resourceId('Microsoft.Network/networkInterfaces', if(contains(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].name, format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex('networkInterfaces')].nicSuffix)))]" } } ] @@ -1006,7 +1028,7 @@ "vm_configurationProfileAssignment": { "condition": "[not(empty(parameters('configurationProfile')))]", "type": "Microsoft.Automanage/configurationProfileAssignments", - "apiVersion": "2021-04-30-preview", + "apiVersion": "2022-05-04", "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", "name": "default", "properties": { @@ -1016,6 +1038,26 @@ "vm" ] }, + "vm_autoShutdownConfiguration": { + "condition": "[not(empty(parameters('autoShutdownConfig')))]", + "type": "Microsoft.DevTestLab/schedules", + "apiVersion": "2018-09-15", + "name": "[format('shutdown-computevm-{0}', parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "status": "[if(contains(parameters('autoShutdownConfig'), 'status'), parameters('autoShutdownConfig').status, 'Disabled')]", + "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]", + "taskType": "ComputeVmShutdownTask", + "dailyRecurrence": { + "time": "[if(contains(parameters('autoShutdownConfig'), 'time'), parameters('autoShutdownConfig').time, '19:00')]" + }, + "timeZoneId": "[if(contains(parameters('autoShutdownConfig'), 'timeZone'), parameters('autoShutdownConfig').timeZone, 'UTC')]", + "notificationSettings": "[if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), createObject('status', if(contains(parameters('autoShutdownConfig'), 'notificationStatus'), parameters('autoShutdownConfig').notificationStatus, 'Disabled'), 'emailRecipient', if(contains(parameters('autoShutdownConfig'), 'notificationEmail'), parameters('autoShutdownConfig').notificationEmail, ''), 'notificationLocale', if(contains(parameters('autoShutdownConfig'), 'notificationLocale'), parameters('autoShutdownConfig').notificationLocale, 'en'), 'timeInMinutes', if(contains(parameters('autoShutdownConfig'), 'notificationWebhookUrl'), parameters('autoShutdownConfig').notificationWebhookUrl, ''), 'webhookUrl', if(contains(parameters('autoShutdownConfig'), 'notificationTimeInMinutes'), parameters('autoShutdownConfig').notificationTimeInMinutes, 30)), null())]" + }, + "dependsOn": [ + "vm" + ] + }, "vm_logAnalyticsWorkspace": { "condition": "[not(empty(tryGet(parameters('extensionMonitoringAgentConfig'), 'monitoringWorkspaceId')))]", "existing": true, @@ -1090,16 +1132,14 @@ }, "mode": "Incremental", "parameters": { - "networkInterfaceName": { - "value": "[format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex()].nicSuffix)]" - }, + "networkInterfaceName": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'name'), createObject('value', parameters('nicConfigurations')[copyIndex()].name), createObject('value', format('{0}{1}', parameters('name'), parameters('nicConfigurations')[copyIndex()].nicSuffix)))]", "virtualMachineName": { "value": "[parameters('name')]" }, "location": { "value": "[parameters('location')]" }, - "enableIPForwarding": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), if(not(empty(parameters('nicConfigurations')[copyIndex()].enableIPForwarding)), createObject('value', parameters('nicConfigurations')[copyIndex()].enableIPForwarding), createObject('value', false())), createObject('value', false()))]", + "enableIPForwarding": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableIPForwarding), createObject('value', false()))]", "enableAcceleratedNetworking": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'enableAcceleratedNetworking'), createObject('value', parameters('nicConfigurations')[copyIndex()].enableAcceleratedNetworking), createObject('value', true()))]", "dnsServers": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'dnsServers'), if(not(empty(parameters('nicConfigurations')[copyIndex()].dnsServers)), createObject('value', parameters('nicConfigurations')[copyIndex()].dnsServers), createObject('value', createArray())), createObject('value', createArray()))]", "networkSecurityGroupResourceId": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('nicConfigurations')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", @@ -1129,8 +1169,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "7272408028164257685" + "version": "0.26.54.24096", + "templateHash": "1849400803354837318" } }, "definitions": { @@ -1419,7 +1459,7 @@ "name": "networkInterface_publicIPAddresses", "count": "[length(parameters('ipConfigurations'))]" }, - "condition": "[contains(parameters('ipConfigurations')[copyIndex()], 'pipconfiguration')]", + "condition": "[contains(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-publicIP-{1}', deployment().name, copyIndex())]", @@ -1430,7 +1470,7 @@ "mode": "Incremental", "parameters": { "name": { - "value": "[format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex()].pipconfiguration.publicIpNameSuffix)]" + "value": "[format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIpNameSuffix)]" }, "diagnosticSettings": { "value": "[tryGet(parameters('ipConfigurations')[copyIndex()], 'diagnosticSettings')]" @@ -1441,16 +1481,16 @@ "lock": { "value": "[parameters('lock')]" }, - "publicIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressVersion'), createObject('value', parameters('ipConfigurations')[copyIndex()].publicIPAddressVersion), createObject('value', 'IPv4'))]", - "publicIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPAllocationMethod'), createObject('value', parameters('ipConfigurations')[copyIndex()].publicIPAllocationMethod), createObject('value', 'Static'))]", - "publicIpPrefixResourceId": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPPrefixResourceId'), createObject('value', parameters('ipConfigurations')[copyIndex()].publicIPPrefixResourceId), createObject('value', ''))]", - "roleAssignments": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'roleAssignments'), createObject('value', parameters('ipConfigurations')[copyIndex()].roleAssignments), createObject('value', createArray()))]", - "skuName": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'skuName'), createObject('value', parameters('ipConfigurations')[copyIndex()].skuName), createObject('value', 'Standard'))]", - "skuTier": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'skuTier'), createObject('value', parameters('ipConfigurations')[copyIndex()].skuTier), createObject('value', 'Regional'))]", + "publicIPAddressVersion": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAddressVersion'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAddressVersion), createObject('value', 'IPv4'))]", + "publicIPAllocationMethod": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPAllocationMethod'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPAllocationMethod), createObject('value', 'Static'))]", + "publicIpPrefixResourceId": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'publicIPPrefixResourceId'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.publicIPPrefixResourceId), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'roleAssignments'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.roleAssignments), createObject('value', createArray()))]", + "skuName": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuName'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuName), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'skuTier'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.skuTier), createObject('value', 'Regional'))]", "tags": { "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'tags'), parameters('tags'))]" }, - "zones": "[if(contains(parameters('ipConfigurations')[copyIndex()], 'zones'), createObject('value', parameters('ipConfigurations')[copyIndex()].zones), createObject('value', createArray('1', '2', '3')))]", + "zones": "[if(contains(parameters('ipConfigurations')[copyIndex()].pipConfiguration, 'zones'), createObject('value', parameters('ipConfigurations')[copyIndex()].pipConfiguration.zones), createObject('value', createArray(1, 2, 3)))]", "enableTelemetry": { "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" } @@ -1462,8 +1502,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "3488076626994379707" + "version": "0.26.54.24096", + "templateHash": "4718335757080871925" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -1512,7 +1552,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, "conditionVersion": { @@ -1606,11 +1646,14 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } } }, "metadata": { - "description": "Required. The DDoS protection plan ID associated with the public IP address." + "description": "Required. The DDoS protection plan associated with the public IP address." } }, "protectionMode": { @@ -1654,12 +1697,19 @@ "metadata": { "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } } }, "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." } }, "metricCategories": { @@ -1672,12 +1722,19 @@ "metadata": { "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } } }, "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." } }, "logAnalyticsDestinationType": { @@ -1758,7 +1815,19 @@ }, "zones": { "type": "array", - "nullable": true, + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], "metadata": { "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." } @@ -1876,7 +1945,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.2.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -1894,7 +1963,7 @@ }, "publicIpAddress": { "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "2023-04-01", + "apiVersion": "2023-09-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -1902,7 +1971,7 @@ "name": "[parameters('skuName')]", "tier": "[parameters('skuTier')]" }, - "zones": "[parameters('zones')]", + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", "properties": { "ddosSettings": "[parameters('ddosSettings')]", "dnsSettings": "[parameters('dnsSettings')]", @@ -1910,7 +1979,7 @@ "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", - "ipTags": [] + "ipTags": null } }, "publicIpAddress_lock": { @@ -1959,12 +2028,30 @@ "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", - "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())))]", "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, @@ -2000,14 +2087,14 @@ "metadata": { "description": "The public IP address of the public IP address resource." }, - "value": "[if(contains(reference('publicIpAddress'), 'ipAddress'), reference('publicIpAddress').ipAddress, '')]" + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('publicIpAddress', '2023-04-01', 'full').location]" + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" } } } @@ -2031,7 +2118,7 @@ { "name": "value", "count": "[length(parameters('ipConfigurations'))]", - "input": "[createObject('name', if(not(empty(parameters('ipConfigurations')[copyIndex('value')].name)), parameters('ipConfigurations')[copyIndex('value')].name, null()), 'primary', equals(copyIndex('value'), 0), 'privateIPAllocationMethod', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod, null()), null()), 'privateIPAddress', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('value')].privateIPAddress, null()), null()), 'publicIPAddressResourceId', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'pipconfiguration'), resourceId('Microsoft.Network/publicIPAddresses', format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex('value')].pipconfiguration.publicIpNameSuffix)), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerBackendAddressPools, null()), 'applicationSecurityGroups', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('value')].applicationSecurityGroups, null()), 'applicationGatewayBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].applicationGatewayBackendAddressPools, null()), 'gatewayLoadBalancer', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('value')].gatewayLoadBalancer, null()), 'loadBalancerInboundNatRules', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerInboundNatRules, null()), 'privateIPAddressVersion', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('value')].privateIPAddressVersion, null()), 'virtualNetworkTaps', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('value')].virtualNetworkTaps, null()))]" + "input": "[createObject('name', if(not(empty(parameters('ipConfigurations')[copyIndex('value')].name)), parameters('ipConfigurations')[copyIndex('value')].name, null()), 'primary', equals(copyIndex('value'), 0), 'privateIPAllocationMethod', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod)), parameters('ipConfigurations')[copyIndex('value')].privateIPAllocationMethod, null()), null()), 'privateIPAddress', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), if(not(empty(parameters('ipConfigurations')[copyIndex('value')].privateIPAddress)), parameters('ipConfigurations')[copyIndex('value')].privateIPAddress, null()), null()), 'publicIPAddressResourceId', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), resourceId('Microsoft.Network/publicIPAddresses', format('{0}{1}', parameters('virtualMachineName'), parameters('ipConfigurations')[copyIndex('value')].pipConfiguration.publicIpNameSuffix)), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerBackendAddressPools, null()), 'applicationSecurityGroups', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), parameters('ipConfigurations')[copyIndex('value')].applicationSecurityGroups, null()), 'applicationGatewayBackendAddressPools', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), parameters('ipConfigurations')[copyIndex('value')].applicationGatewayBackendAddressPools, null()), 'gatewayLoadBalancer', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), parameters('ipConfigurations')[copyIndex('value')].gatewayLoadBalancer, null()), 'loadBalancerInboundNatRules', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), parameters('ipConfigurations')[copyIndex('value')].loadBalancerInboundNatRules, null()), 'privateIPAddressVersion', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), parameters('ipConfigurations')[copyIndex('value')].privateIPAddressVersion, null()), 'virtualNetworkTaps', if(contains(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'), parameters('ipConfigurations')[copyIndex('value')].virtualNetworkTaps, null()))]" } ] }, @@ -2067,8 +2154,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "14617314094453038306" + "version": "0.25.53.49325", + "templateHash": "1612343535299711142" }, "name": "Network Interface", "description": "This module deploys a Network Interface.", @@ -2105,12 +2192,19 @@ "metadata": { "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } } }, "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." } }, "metricCategories": { @@ -2123,12 +2217,19 @@ "metadata": { "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } } } }, "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." } }, "logAnalyticsDestinationType": { @@ -2223,7 +2324,7 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." } }, "conditionVersion": { @@ -2404,7 +2505,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-networkinterface.{0}.{1}', replace('0.2.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-networkinterface.{0}.{1}', replace('0.2.4', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2485,11 +2586,21 @@ "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "metrics": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics', 'timeGrain', null(), 'enabled', true())))]", "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, @@ -2599,8 +2710,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -2805,8 +2916,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3007,8 +3118,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3221,8 +3332,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3405,9 +3516,14 @@ "value": "Microsoft.Azure.Monitoring.DependencyAgent" }, "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'DependencyAgentWindows'), createObject('value', 'DependencyAgentLinux'))]", - "typeHandlerVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDependencyAgentConfig').typeHandlerVersion), createObject('value', '9.5'))]", + "typeHandlerVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), createObject('value', parameters('extensionDependencyAgentConfig').typeHandlerVersion), createObject('value', '9.10'))]", "autoUpgradeMinorVersion": "[if(contains(parameters('extensionDependencyAgentConfig'), 'autoUpgradeMinorVersion'), createObject('value', parameters('extensionDependencyAgentConfig').autoUpgradeMinorVersion), createObject('value', true()))]", "enableAutomaticUpgrade": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionDependencyAgentConfig').enableAutomaticUpgrade), createObject('value', true()))]", + "settings": { + "value": { + "enableAMA": "[if(contains(parameters('extensionDependencyAgentConfig'), 'enableAMA'), parameters('extensionDependencyAgentConfig').enableAMA, true())]" + } + }, "tags": { "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'tags'), parameters('tags'))]" } @@ -3419,8 +3535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3616,8 +3732,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3817,8 +3933,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4026,8 +4142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4227,8 +4343,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4426,8 +4542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4636,8 +4752,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4836,8 +4952,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8063436714999902780" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -5035,8 +5151,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "2393851439891433510" + "version": "0.26.54.24096", + "templateHash": "5385249890312845255" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", @@ -5170,14 +5286,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('vm', '2022-11-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('vm', '2023-09-01', 'full'), 'identity'), 'principalId'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('vm', '2022-11-01', 'full').location]" + "value": "[reference('vm', '2023-09-01', 'full').location]" } } } \ No newline at end of file diff --git a/avm/res/compute/virtual-machine/modules/nic-configuration.bicep b/avm/res/compute/virtual-machine/modules/nic-configuration.bicep index 8b308b2f8d..6720238a34 100644 --- a/avm/res/compute/virtual-machine/modules/nic-configuration.bicep +++ b/avm/res/compute/virtual-machine/modules/nic-configuration.bicep @@ -27,48 +27,87 @@ param diagnosticSettings diagnosticSettingType @description('Optional. Array of role assignments to create.') param roleAssignments roleAssignmentType -module networkInterface_publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.2.1' = [for (ipConfiguration, index) in ipConfigurations: if (contains(ipConfiguration, 'pipconfiguration')) { - name: '${deployment().name}-publicIP-${index}' - params: { - name: '${virtualMachineName}${ipConfiguration.pipconfiguration.publicIpNameSuffix}' - diagnosticSettings: ipConfiguration.?diagnosticSettings - location: location - lock: lock - publicIPAddressVersion: contains(ipConfiguration, 'publicIPAddressVersion') ? ipConfiguration.publicIPAddressVersion : 'IPv4' - publicIPAllocationMethod: contains(ipConfiguration, 'publicIPAllocationMethod') ? ipConfiguration.publicIPAllocationMethod : 'Static' - publicIpPrefixResourceId: contains(ipConfiguration, 'publicIPPrefixResourceId') ? ipConfiguration.publicIPPrefixResourceId : '' - roleAssignments: contains(ipConfiguration, 'roleAssignments') ? ipConfiguration.roleAssignments : [] - skuName: contains(ipConfiguration, 'skuName') ? ipConfiguration.skuName : 'Standard' - skuTier: contains(ipConfiguration, 'skuTier') ? ipConfiguration.skuTier : 'Regional' - tags: ipConfiguration.?tags ?? tags - zones: contains(ipConfiguration, 'zones') ? ipConfiguration.zones : [ - '1' - '2' - '3' - ] - enableTelemetry: ipConfiguration.?enableTelemetry ?? enableTelemetry +module networkInterface_publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.4.0' = [ + for (ipConfiguration, index) in ipConfigurations: if (contains(ipConfiguration, 'pipConfiguration')) { + name: '${deployment().name}-publicIP-${index}' + params: { + name: '${virtualMachineName}${ipConfiguration.pipConfiguration.publicIpNameSuffix}' + diagnosticSettings: ipConfiguration.?diagnosticSettings + location: location + lock: lock + publicIPAddressVersion: contains(ipConfiguration.pipConfiguration, 'publicIPAddressVersion') + ? ipConfiguration.pipConfiguration.publicIPAddressVersion + : 'IPv4' + publicIPAllocationMethod: contains(ipConfiguration.pipConfiguration, 'publicIPAllocationMethod') + ? ipConfiguration.pipConfiguration.publicIPAllocationMethod + : 'Static' + publicIpPrefixResourceId: contains(ipConfiguration.pipConfiguration, 'publicIPPrefixResourceId') + ? ipConfiguration.pipConfiguration.publicIPPrefixResourceId + : '' + roleAssignments: contains(ipConfiguration.pipConfiguration, 'roleAssignments') + ? ipConfiguration.pipConfiguration.roleAssignments + : [] + skuName: contains(ipConfiguration.pipConfiguration, 'skuName') + ? ipConfiguration.pipConfiguration.skuName + : 'Standard' + skuTier: contains(ipConfiguration.pipConfiguration, 'skuTier') + ? ipConfiguration.pipConfiguration.skuTier + : 'Regional' + tags: ipConfiguration.?tags ?? tags + zones: contains(ipConfiguration.pipConfiguration, 'zones') + ? ipConfiguration.pipConfiguration.zones + : [ + 1 + 2 + 3 + ] + enableTelemetry: ipConfiguration.?enableTelemetry ?? enableTelemetry + } } -}] +] -module networkInterface 'br/public:avm/res/network/network-interface:0.2.2' = { +module networkInterface 'br/public:avm/res/network/network-interface:0.2.4' = { name: '${deployment().name}-NetworkInterface' params: { name: networkInterfaceName - ipConfigurations: [for (ipConfiguration, index) in ipConfigurations: { - name: !empty(ipConfiguration.name) ? ipConfiguration.name : null - primary: index == 0 - privateIPAllocationMethod: contains(ipConfiguration, 'privateIPAllocationMethod') ? (!empty(ipConfiguration.privateIPAllocationMethod) ? ipConfiguration.privateIPAllocationMethod : null) : null - privateIPAddress: contains(ipConfiguration, 'privateIPAddress') ? (!empty(ipConfiguration.privateIPAddress) ? ipConfiguration.privateIPAddress : null) : null - publicIPAddressResourceId: contains(ipConfiguration, 'pipconfiguration') ? resourceId('Microsoft.Network/publicIPAddresses', '${virtualMachineName}${ipConfiguration.pipconfiguration.publicIpNameSuffix}') : null - subnetResourceId: ipConfiguration.subnetResourceId - loadBalancerBackendAddressPools: contains(ipConfiguration, 'loadBalancerBackendAddressPools') ? ipConfiguration.loadBalancerBackendAddressPools : null - applicationSecurityGroups: contains(ipConfiguration, 'applicationSecurityGroups') ? ipConfiguration.applicationSecurityGroups : null - applicationGatewayBackendAddressPools: contains(ipConfiguration, 'applicationGatewayBackendAddressPools') ? ipConfiguration.applicationGatewayBackendAddressPools : null - gatewayLoadBalancer: contains(ipConfiguration, 'gatewayLoadBalancer') ? ipConfiguration.gatewayLoadBalancer : null - loadBalancerInboundNatRules: contains(ipConfiguration, 'loadBalancerInboundNatRules') ? ipConfiguration.loadBalancerInboundNatRules : null - privateIPAddressVersion: contains(ipConfiguration, 'privateIPAddressVersion') ? ipConfiguration.privateIPAddressVersion : null - virtualNetworkTaps: contains(ipConfiguration, 'virtualNetworkTaps') ? ipConfiguration.virtualNetworkTaps : null - }] + ipConfigurations: [ + for (ipConfiguration, index) in ipConfigurations: { + name: !empty(ipConfiguration.name) ? ipConfiguration.name : null + primary: index == 0 + privateIPAllocationMethod: contains(ipConfiguration, 'privateIPAllocationMethod') + ? (!empty(ipConfiguration.privateIPAllocationMethod) ? ipConfiguration.privateIPAllocationMethod : null) + : null + privateIPAddress: contains(ipConfiguration, 'privateIPAddress') + ? (!empty(ipConfiguration.privateIPAddress) ? ipConfiguration.privateIPAddress : null) + : null + publicIPAddressResourceId: contains(ipConfiguration, 'pipConfiguration') + ? resourceId( + 'Microsoft.Network/publicIPAddresses', + '${virtualMachineName}${ipConfiguration.pipConfiguration.publicIpNameSuffix}' + ) + : null + subnetResourceId: ipConfiguration.subnetResourceId + loadBalancerBackendAddressPools: contains(ipConfiguration, 'loadBalancerBackendAddressPools') + ? ipConfiguration.loadBalancerBackendAddressPools + : null + applicationSecurityGroups: contains(ipConfiguration, 'applicationSecurityGroups') + ? ipConfiguration.applicationSecurityGroups + : null + applicationGatewayBackendAddressPools: contains(ipConfiguration, 'applicationGatewayBackendAddressPools') + ? ipConfiguration.applicationGatewayBackendAddressPools + : null + gatewayLoadBalancer: contains(ipConfiguration, 'gatewayLoadBalancer') + ? ipConfiguration.gatewayLoadBalancer + : null + loadBalancerInboundNatRules: contains(ipConfiguration, 'loadBalancerInboundNatRules') + ? ipConfiguration.loadBalancerInboundNatRules + : null + privateIPAddressVersion: contains(ipConfiguration, 'privateIPAddressVersion') + ? ipConfiguration.privateIPAddressVersion + : null + virtualNetworkTaps: contains(ipConfiguration, 'virtualNetworkTaps') ? ipConfiguration.virtualNetworkTaps : null + } + ] location: location tags: tags diagnosticSettings: diagnosticSettings diff --git a/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep index 47e5049f6d..32e5a29dec 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep @@ -71,7 +71,7 @@ module testDeployment '../../../main.bicep' = [ sku: '22_04-lts-gen2' version: 'latest' } - availabilityZone: 0 + zone: 0 nicConfigurations: [ { ipConfigurations: [ @@ -79,12 +79,12 @@ module testDeployment '../../../main.bicep' = [ name: 'ipconfig01' pipConfiguration: { publicIpNameSuffix: '-pip-01' + zones: [ + 1 + 2 + 3 + ] } - zones: [ - '1' - '2' - '3' - ] subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep index 6f646b2f4b..b4ec9c6b81 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep @@ -67,16 +67,16 @@ module testDeployment '../../../main.bicep' = [ sku: '22_04-lts-gen2' version: 'latest' } - availabilityZone: 0 + zone: 0 nicConfigurations: [ { ipConfigurations: [ { name: 'ipconfig01' + subnetResourceId: nestedDependencies.outputs.subnetResourceId pipConfiguration: { publicIpNameSuffix: '-pip-01' } - subnetResourceId: nestedDependencies.outputs.subnetResourceId } ] nicSuffix: '-nic-01' diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep index 594807efaf..2253dba10d 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep @@ -220,10 +220,10 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } resource backupServiceKeyVaultPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${keyVault.name}-${location}-94d1f9d9-9caf-4e06-809e-29fc95f69e65-KeyVault-KeyVaultAdministrator-RoleAssignment') + name: guid('${keyVault.name}-${location}-268f6a53-9f68-4a38-ae47-166f730d86af-KeyVault-KeyVaultAdministrator-RoleAssignment') scope: keyVault properties: { - principalId: '94d1f9d9-9caf-4e06-809e-29fc95f69e65' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) + principalId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483' diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep index 4fa2f77428..a2d2d73750 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep @@ -99,6 +99,11 @@ module testDeployment '../../../main.bicep' = { name: 'ipconfig01' pipConfiguration: { publicIpNameSuffix: '-pip-01' + zones: [ + 1 + 2 + 3 + ] roleAssignments: [ { roleDefinitionIdOrName: 'Reader' @@ -107,11 +112,6 @@ module testDeployment '../../../main.bicep' = { } ] } - zones: [ - '1' - '2' - '3' - ] subnetResourceId: nestedDependencies.outputs.subnetResourceId diagnosticSettings: [ { @@ -129,7 +129,7 @@ module testDeployment '../../../main.bicep' = { ] } ] - nicSuffix: '-nic-01' + name: 'nic-test-01' roleAssignments: [ { roleDefinitionIdOrName: 'Reader' @@ -165,7 +165,7 @@ module testDeployment '../../../main.bicep' = { } osType: 'Linux' vmSize: 'Standard_DS2_v2' - availabilityZone: 1 + zone: 1 backupPolicyName: nestedDependencies.outputs.recoveryServicesVaultBackupPolicyName backupVaultName: nestedDependencies.outputs.recoveryServicesVaultName backupVaultResourceGroup: nestedDependencies.outputs.recoveryServicesVaultResourceGroupName @@ -193,6 +193,7 @@ module testDeployment '../../../main.bicep' = { ] enableAutomaticUpdates: true patchMode: 'AutomaticByPlatform' + rebootSetting: 'IfRequired' disablePasswordAuthentication: true encryptionAtHost: false extensionCustomScriptConfig: { @@ -214,6 +215,7 @@ module testDeployment '../../../main.bicep' = { } extensionDependencyAgentConfig: { enabled: true + enableAMA: true tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep index f9979cbc71..5810b063fb 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep @@ -217,10 +217,10 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } resource backupServiceKeyVaultPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${keyVault.name}-${location}-94d1f9d9-9caf-4e06-809e-29fc95f69e65-KeyVault-KeyVaultAdministrator-RoleAssignment') + name: guid('${keyVault.name}-${location}-268f6a53-9f68-4a38-ae47-166f730d86af-KeyVault-KeyVaultAdministrator-RoleAssignment') scope: keyVault properties: { - principalId: '94d1f9d9-9caf-4e06-809e-29fc95f69e65' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) + principalId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483' diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep index 4090a9830a..e99ba9c28c 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep @@ -104,6 +104,11 @@ module testDeployment '../../../main.bicep' = [ name: 'ipconfig01' pipConfiguration: { publicIpNameSuffix: '-pip-01' + zones: [ + 1 + 2 + 3 + ] roleAssignments: [ { roleDefinitionIdOrName: 'Reader' @@ -112,11 +117,6 @@ module testDeployment '../../../main.bicep' = [ } ] } - zones: [ - '1' - '2' - '3' - ] subnetResourceId: nestedDependencies.outputs.subnetResourceId diagnosticSettings: [ { @@ -170,7 +170,7 @@ module testDeployment '../../../main.bicep' = [ osType: 'Windows' vmSize: 'Standard_DS2_v2' adminPassword: password - availabilityZone: 2 + zone: 2 backupPolicyName: nestedDependencies.outputs.recoveryServicesVaultBackupPolicyName backupVaultName: nestedDependencies.outputs.recoveryServicesVaultName backupVaultResourceGroup: nestedDependencies.outputs.recoveryServicesVaultResourceGroupName @@ -239,6 +239,7 @@ module testDeployment '../../../main.bicep' = [ } extensionDependencyAgentConfig: { enabled: true + enableAMA: true tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep index 5e6f6365dd..7232384320 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep @@ -62,7 +62,7 @@ module testDeployment '../../../main.bicep' = [ sku: '2022-datacenter-azure-edition' version: 'latest' } - availabilityZone: 0 + zone: 0 nicConfigurations: [ { ipConfigurations: [ diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep index c676412329..0db2535978 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep @@ -65,7 +65,7 @@ module testDeployment '../../../main.bicep' = [ sku: '2022-datacenter-azure-edition' version: 'latest' } - availabilityZone: 0 + zone: 0 nicConfigurations: [ { ipConfigurations: [ diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep index 1c198d0c69..e13b61d4e3 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep @@ -66,7 +66,7 @@ module testDeployment '../../../main.bicep' = [ managedIdentities: { systemAssigned: true } - availabilityZone: 0 + zone: 0 imageReference: { publisher: 'MicrosoftWindowsServer' offer: 'WindowsServer' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep index f9979cbc71..5810b063fb 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep @@ -217,10 +217,10 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } resource backupServiceKeyVaultPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${keyVault.name}-${location}-94d1f9d9-9caf-4e06-809e-29fc95f69e65-KeyVault-KeyVaultAdministrator-RoleAssignment') + name: guid('${keyVault.name}-${location}-268f6a53-9f68-4a38-ae47-166f730d86af-KeyVault-KeyVaultAdministrator-RoleAssignment') scope: keyVault properties: { - principalId: '94d1f9d9-9caf-4e06-809e-29fc95f69e65' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) + principalId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep index 633d6116f2..95bf406d23 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep @@ -104,6 +104,11 @@ module testDeployment '../../../main.bicep' = [ name: 'ipconfig01' pipConfiguration: { publicIpNameSuffix: '-pip-01' + zones: [ + 1 + 2 + 3 + ] roleAssignments: [ { roleDefinitionIdOrName: 'Reader' @@ -112,11 +117,6 @@ module testDeployment '../../../main.bicep' = [ } ] } - zones: [ - '1' - '2' - '3' - ] subnetResourceId: nestedDependencies.outputs.subnetResourceId diagnosticSettings: [ { @@ -134,7 +134,8 @@ module testDeployment '../../../main.bicep' = [ ] } ] - nicSuffix: '-nic-01' + name: 'nic-test-01' + enableIPForwarding: true roleAssignments: [ { roleDefinitionIdOrName: 'Reader' @@ -171,7 +172,7 @@ module testDeployment '../../../main.bicep' = [ osType: 'Windows' vmSize: 'Standard_DS2_v2' adminPassword: password - availabilityZone: 2 + zone: 2 backupPolicyName: nestedDependencies.outputs.recoveryServicesVaultBackupPolicyName backupVaultName: nestedDependencies.outputs.recoveryServicesVaultName backupVaultResourceGroup: nestedDependencies.outputs.recoveryServicesVaultResourceGroupName @@ -201,7 +202,17 @@ module testDeployment '../../../main.bicep' = [ ] enableAutomaticUpdates: true patchMode: 'AutomaticByPlatform' + rebootSetting: 'IfRequired' encryptionAtHost: false + autoShutdownConfig: { + status: 'Enabled' + time: '19:00' + timeZone: 'UTC' + notificationStatus: 'Enabled' + notificationEmail: 'test@contoso.com' + notificationLocale: 'en' + notificationTimeInMinutes: 30 + } extensionAntiMalwareConfig: { enabled: true settings: { @@ -244,6 +255,7 @@ module testDeployment '../../../main.bicep' = [ } extensionDependencyAgentConfig: { enabled: true + enableAMA: true tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep index 603cf8c4fc..271127387b 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-compute.virtualMachines-${se param resourceLocation string = 'eastus' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'cvmwinnvidia' +param serviceShort string = 'cvmwinnv' @description('Optional. The password to leverage for the login.') @secure() @@ -65,7 +65,7 @@ module testDeployment '../../../main.bicep' = [ sku: '2022-datacenter-azure-edition' version: 'latest' } - availabilityZone: 0 + zone: 0 nicConfigurations: [ { ipConfigurations: [ diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep index cffd564ce1..e3edaf2352 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep @@ -68,7 +68,7 @@ module testDeployment '../../../main.bicep' = [ sku: '2019-datacenter' version: 'latest' } - availabilityZone: 0 + zone: 0 nicConfigurations: [ { ipConfigurations: [ diff --git a/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1 b/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1 index f0f36ce384..15532c48ed 100644 --- a/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1 +++ b/avm/res/compute/virtual-machine/tests/unit/avm.core.team.tests.ps1 @@ -26,14 +26,10 @@ BeforeAll { } Describe 'AVM Core Team Module Specific Tests' { - Context 'WAF - Reliability Pillar - Parameter Tests' { - It 'VM Module Availability Zone Parameter Should Not Have A Default Value Set' { - $isRequired = Get-IsParameterRequired -TemplateFileContent $moduleJsonContentHashtable -Parameter $moduleJsonContentHashtable.parameters.availabilityZone + $isRequired = Get-IsParameterRequired -TemplateFileContent $moduleJsonContentHashtable -Parameter $moduleJsonContentHashtable.parameters.zone $isRequired | Should -Be $true } - } - } diff --git a/avm/res/compute/virtual-machine/version.json b/avm/res/compute/virtual-machine/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/compute/virtual-machine/version.json +++ b/avm/res/compute/virtual-machine/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/front-door/README.md b/avm/res/network/front-door/README.md index 3ca3f86595..06dd319027 100644 --- a/avm/res/network/front-door/README.md +++ b/avm/res/network/front-door/README.md @@ -639,10 +639,10 @@ module frontDoor 'br/public:avm/res/network/front-door:' = { { name: 'heathProbe' properties: { - enabledState: '' - healthProbeMethod: '' + enabledState: 'Enabled' + healthProbeMethod: 'HEAD' intervalInSeconds: 60 - path: '/' + path: '/healthz' protocol: 'Https' } } @@ -765,10 +765,10 @@ module frontDoor 'br/public:avm/res/network/front-door:' = { { "name": "heathProbe", "properties": { - "enabledState": "", - "healthProbeMethod": "", + "enabledState": "Enabled", + "healthProbeMethod": "HEAD", "intervalInSeconds": 60, - "path": "/", + "path": "/healthz", "protocol": "Https" } } diff --git a/avm/res/network/front-door/main.json b/avm/res/network/front-door/main.json index 067451e5f1..532598ea9a 100644 --- a/avm/res/network/front-door/main.json +++ b/avm/res/network/front-door/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11688367630078848691" + "version": "0.26.170.59819", + "templateHash": "10103314410755472523" }, "name": "Azure Front Doors", "description": "This module deploys an Azure Front Door.", diff --git a/avm/res/network/front-door/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/front-door/tests/e2e/waf-aligned/main.test.bicep index fd7839f280..f05335784f 100644 --- a/avm/res/network/front-door/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/front-door/tests/e2e/waf-aligned/main.test.bicep @@ -32,7 +32,7 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { } // Diagnostics -// =========== +// ============ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' @@ -99,10 +99,10 @@ module testDeployment '../../../main.bicep' = [ { name: 'heathProbe' properties: { - enabledState: '' - healthProbeMethod: '' + enabledState: 'Enabled' + healthProbeMethod: 'HEAD' intervalInSeconds: 60 - path: '/' + path: '/healthz' protocol: 'Https' } } diff --git a/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep b/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep index 677a80dbe1..3e801241ff 100644 --- a/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep +++ b/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep @@ -12,6 +12,7 @@ metadata description = 'This instance deploys the module with the minimum set of param resourceGroupName string = 'dep-${namePrefix}-sql.instancepool-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') +#disable-next-line no-unused-params // A rotation location cannot be used for this test as the SQL Instance Pool deletion is not possible in the same deployment param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') @@ -21,6 +22,9 @@ param serviceShort string = 'sipmin' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') param namePrefix string = '#_namePrefix_#' +@description('Optional. The static location of the resource group & resources.') // Note, we set the location of the SQL Instance Pool to avoid conflicts with the already existing ones +param tempLocation string = 'uksouth' + // ============ // // Dependencies // // ============ // @@ -29,12 +33,12 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: tempLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, tempLocation)}-nestedDependencies' params: { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' @@ -42,7 +46,7 @@ module nestedDependencies 'dependencies.bicep' = { nsgName: 'dep-${namePrefix}-nsg-${serviceShort}' routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' sqlInstancePoolName: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: tempLocation } } @@ -52,10 +56,10 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, tempLocation)}-test-${serviceShort}' params: { name: nestedDependencies.outputs.sqlInstancePoolName - location: resourceLocation + location: tempLocation subnetResourceId: nestedDependencies.outputs.subnetId } } diff --git a/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep index ffaa207271..943e45acec 100644 --- a/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep @@ -12,6 +12,7 @@ metadata description = 'This instance deploys the module in alignment with the b param resourceGroupName string = 'dep-${namePrefix}-sql.instancepool-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') +#disable-next-line no-unused-params // A rotation location cannot be used for this test as the SQL Instance Pool deletion is not possible in the same deployment param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') @@ -21,6 +22,9 @@ param serviceShort string = 'sipwaf' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') param namePrefix string = '#_namePrefix_#' +@description('Optional. The static location of the resource group & resources.') // Note, we set the location of the SQL Instance Pool to avoid conflicts with the already existing ones +param tempLocation string = 'northeurope' + // ============ // // Dependencies // // ============ // @@ -29,12 +33,12 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: tempLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, tempLocation)}-nestedDependencies' params: { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' @@ -42,7 +46,7 @@ module nestedDependencies 'dependencies.bicep' = { nsgName: 'dep-${namePrefix}-nsg-${serviceShort}' routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' sqlInstancePoolName: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: tempLocation } } @@ -52,10 +56,10 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + name: '${uniqueString(deployment().name, tempLocation)}-test-${serviceShort}' params: { name: nestedDependencies.outputs.sqlInstancePoolName - location: resourceLocation + location: tempLocation skuName: 'GP_Gen8IM' subnetResourceId: nestedDependencies.outputs.subnetId } diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 index 3ef3637972..d2d7b20cb1 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 @@ -18,7 +18,7 @@ Mandatory. The URL of the GitHub issue, like 'https://github.com/Azure/bicep-reg Set-AvmGitHubIssueOwnerConfig -Repo 'Azure/bicep-registry-modules' -IssueUrl 'https://github.com/Azure/bicep-registry-modules/issues/757' .NOTES -Will be triggered by the workflow avm.platform.set-avm-github-issue-owner-config.yml +Will be triggered by the workflow platform.set-avm-github-issue-owner-config.yml #> function Set-AvmGitHubIssueOwnerConfig { [CmdletBinding(SupportsShouldProcess)] @@ -74,7 +74,7 @@ function Set-AvmGitHubIssueOwnerConfig { **@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** > [!IMPORTANT] -> A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleContributorsGHTeam) team will review it soon! +> A member of the @Azure/$($module.ModuleOwnersGHTeam) or @Azure/$($module.ModuleContributorsGHTeam) team will review it soon! "@ } diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index fed872e51d..03f5f4f95c 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -26,7 +26,7 @@ Set-AvmGithubIssueForWorkflow -Repo 'owner/repo01' -LimitNumberOfRuns 100 -Limit Check the last 100 workflow runs in the repository 'owner/repo01' that happened in the last 2 days. If the workflow name is 'Pipeline 01', then ignore the workflow run. .NOTES -Will be triggered by the workflow avm.platform.manage-workflow-issue.yml +Will be triggered by the workflow platform.manage-workflow-issue.yml #> function Set-AvmGithubIssueForWorkflow { [CmdletBinding(SupportsShouldProcess)] @@ -109,7 +109,7 @@ function Set-AvmGithubIssueForWorkflow { $ProjectNumber = 566 # AVM - Module Issues $comment = @" > [!IMPORTANT] -> @$($module.ModuleOwnersGHTeam), the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. If you are not able to do so, please inform the AVM core team to take over. +> @Azure/$($module.ModuleOwnersGHTeam), the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. If you are not able to do so, please inform the AVM core team to take over. "@ # assign owner $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo @@ -121,7 +121,7 @@ function Set-AvmGithubIssueForWorkflow { > This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support). "@ - gh issue comment $issue.url --body $reply --repo $Repo + gh issue comment $issue.url --body $comment --repo $Repo } } } diff --git a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 index 7cdfa21e8d..7a2af595fa 100644 --- a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 +++ b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 @@ -15,7 +15,7 @@ Optional. Path to the root of the repository. Sync-AvmModulesList -Repo 'Azure/bicep-registry-modules' .NOTES -Will be triggered by the workflow avm.platform.sync-avm-modules-list.yml +Will be triggered by the workflow platform.sync-avm-modules-list.yml #> function Sync-AvmModulesList { param ( diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml index dbea639dbf..ed2666de72 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml @@ -31,6 +31,10 @@ spec: # Azure App Service - Azure.AppService.WebProbe # Supressed as the probe path is specific to the app - Azure.AppService.WebProbePath # Supressed as the probe path is specific to the app + # Azure Front Door + - Azure.FrontDoor.Probe # Supressed as the probe is being provided as parameter and we are not able to enforce as default value + - Azure.FrontDoor.ProbeMethod # Supressed as the probe method is being provided as parameter and we are not able to enforce as default value + - Azure.FrontDoor.ProbePath # Supressed as the probe path is being provided as parameter and we are not able to enforce as default value if: name: "." contains: diff --git a/scripts/azure-pipelines/Find-ChangedModule.ps1 b/scripts/azure-pipelines/Find-ChangedModule.ps1 deleted file mode 100644 index 000c25e6c2..0000000000 --- a/scripts/azure-pipelines/Find-ChangedModule.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -<# -.SYNOPSIS - Finds changed module. - -.DESCRIPTION - The script finds the changed module in the pull request that triggers the pipeline. - -.PARAMETER GitHubToken - The GitHub personal access token to use for authentication. - -.PARAMETER Repository - The Bicep registry module repository name. - -.PARAMETER PullRequestNumber - The pull request number. -#> -param( - # TODO: remove the PAT once the Bicep registry repo goes public. - [Parameter(mandatory = $True)] - [string]$GitHubToken, - - [Parameter(mandatory = $True)] - [string]$Repository, - - [Parameter(mandatory = $True)] - [string]$PullRequestNumber -) - -Import-Module .\scripts\azure-pipelines\utils\AzurePipelinesUtils.psm1 -Force - -$pullRequestFiles = Invoke-RestMethod ` - -Uri "https://api.github.com/repos/$Repository/pulls/$PullRequestNumber/files" ` - -Authentication OAuth ` - -Token (ConvertTo-SecureString $GitHubToken -AsPlainText -Force) - -$separator = [IO.Path]::DirectorySeparatorChar -$changedModuleDirectories = @( - $pullRequestFiles | - Where-Object { $_.filename.StartsWith("modules") } | # Get changed module files. - ForEach-Object { Split-Path $_.filename } | # Get directories of changed module files. - Where-Object { $_.Split($separator).Length -ge 3 } | # Ignore changes outside a module folder, e.g., modules/bicepconfig.json. - ForEach-Object { - $_.Split($separator) | - Select-Object -First 3 | - Join-String -Separator $separator - } | # Get module root directories. - Select-Object -Unique | # Remove duplicates. - Where-Object { Test-Path $_ } # Ignore removed directories. -) - -# If no test file or more than one test file was found, set an empty string to skip the subsequent steps. -$changedModuleDirectory = if ($changedModuleDirectories.Length -eq 1) { $changedModuleDirectories[0] } else { "" } -Set-AzurePipelinesVariable -VariableName "ChangedModuleDirectory" -VariableValue $changedModuleDirectory diff --git a/scripts/azure-pipelines/Get-TargetScope.ps1 b/scripts/azure-pipelines/Get-TargetScope.ps1 deleted file mode 100644 index 52c872c648..0000000000 --- a/scripts/azure-pipelines/Get-TargetScope.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -<# -.SYNOPSIS - Gets the target scope of the module. - -.DESCRIPTION - The script gets the target scope of the main module file. - -.PARAMETER ChangedModuleDirectory - The directory of the changed module. -#> -param( - [Parameter(mandatory = $True)] - [string]$ChangedModuleDirectory -) - -Import-Module .\scripts\azure-pipelines\utils\AzurePipelinesUtils.psm1 -Force - -$armTemplateFilePath = Join-Path $ChangedModuleDirectory -ChildPath "main.json" -$targetScope = "" - -if (Test-Path $armTemplateFilePath -PathType "Leaf") { - $armTemplateFile = Get-Content -Raw -Path $armTemplateFilePath | ConvertFrom-Json - $armTemplateSchema = $armTemplateFile.'$schema' - $armTemplateSchemaPattern = "https?://schema\.management\.azure\.com/schemas/[0-9a-zA-Z-]+/{0}Template\.json#?" - - $targetScope = switch -Regex ($armTemplateSchema) { - $($armTemplateSchemaPattern -f "deployment") { "resourceGroup" } - $($armTemplateSchemaPattern -f "subscriptionDeployment") { "subscription" } - $($armTemplateSchemaPattern -f "managementGroupDeployment") { "managementGroup" } - $($armTemplateSchemaPattern -f "tenantDeployment") { "tenant" } - default { "" } - } -} - -Set-AzurePipelinesVariable -VariableName "TargetScope" -VariableValue $targetScope diff --git a/scripts/azure-pipelines/Get-TestFile.ps1 b/scripts/azure-pipelines/Get-TestFile.ps1 deleted file mode 100644 index 53ca1ed713..0000000000 --- a/scripts/azure-pipelines/Get-TestFile.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -<# -.SYNOPSIS - Gets the module test file. - -.DESCRIPTION - The script gets path to the test file in the directory of the changed module. - -.PARAMETER ChangedModuleDirectory - The directory of the changed module. -#> -param( - [Parameter(mandatory = $True)] - [string]$ChangedModuleDirectory -) - -Import-Module .\scripts\azure-pipelines\utils\AzurePipelinesUtils.psm1 -Force - -$testFilePath = Join-Path $ChangedModuleDirectory -ChildPath "test\main.test.bicep" -$testFilePath = if (Test-Path $testFilePath -PathType "Leaf") { $testFilePath } else { "" } - -Set-AzurePipelinesVariable -VariableName "TestFilePath" -VariableValue $testFilePath diff --git a/scripts/azure-pipelines/New-TestResourceGroup.ps1 b/scripts/azure-pipelines/New-TestResourceGroup.ps1 deleted file mode 100644 index 11487fcd3d..0000000000 --- a/scripts/azure-pipelines/New-TestResourceGroup.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -<# -.SYNOPSIS - Creates a test resource group. - -.DESCRIPTION - The script creates a resource group and grants a service principal access to that resource group for deploying the test Bicep file. - -.PARAMETER PrincipalId - The service principal ID (object ID). - -.PARAMETER Location - The Location of the test resource group to create. Defaults to "westus". -#> -Param( - [Parameter(mandatory = $true)] - [string]$PrincipalId, - - [string]$Location = "westus" -) - -Import-Module .\scripts\azure-pipelines\utils\AzurePipelinesUtils.psm1 -Force -Import-Module .\scripts\azure-pipelines\utils\AzureResourceUtils.psm1 -Force - -Invoke-AzurePipelinesTask { - $pullRequestNumber = $env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER - $commitId = $env:BUILD_SOURCEVERSION.Substring(0, 7) - $timestamp = Get-Date -Format "yyyyMMddHHmmss" -AsUTC - $guid = [GUID]::NewGuid().ToString('N') - $resourceGroupName = "$pullRequestNumber-$commitId-$timestamp-$guid" - - # Create the resource group and wait for replication. - New-AzResourceGroup -Name $resourceGroupName -Location $Location -Verbose - Wait-Replication { (Get-AzResourceGroup -Name $resourceGroupName -Verbose -ErrorAction "SilentlyContinue") -ne $null } - Set-AzurePipelinesVariable -VariableName "ResourceGroupName" -VariableValue $resourceGroupName - - Write-Host "Granting service principal $PrincipalId access to resource group $resourceGroupName..." - - # Can only use -ObjectId instead of -ApplicationId because the connected service principal doesn't have AAD read permision. - $roleAssignment = New-AzRoleAssignment -ResourceGroupName $resourceGroupName -ObjectId $PrincipalId -RoleDefinitionName "Owner" - $roleAssignmentPath = "$($roleAssignment.RoleAssignmentId)?api-version=2021-04-01-preview" - - # Sometimes it takes a while for an RBAC change to propagate. Calling Wait-Replication to ensure - # 6 succesive successful GET operations on the role assignment to reduce the chance of getting - # RBAC errors later due to slow replication. - Wait-Replication { - $getResult = Invoke-AzRestMethod -Method "GET" -Path $roleAssignmentPath - Write-Host $getResult.StatusCode "GET" $roleAssignmentPath - - $getResult.StatusCode -eq 200 - } -SuccessCount 6 -DelayInSeconds 4 -} diff --git a/scripts/azure-pipelines/Remove-TestResourceGroup.ps1 b/scripts/azure-pipelines/Remove-TestResourceGroup.ps1 deleted file mode 100644 index 2874109920..0000000000 --- a/scripts/azure-pipelines/Remove-TestResourceGroup.ps1 +++ /dev/null @@ -1,80 +0,0 @@ -<# -.SYNOPSIS - Removes a test resource group. - -.DESCRIPTION - This script attempts to remove a resource group. If the resource group cannot be removed, it will try cleaning up all the things - that prevent removing resource groups, such as resource locks, backup protection, geo-pairing, etc. The source code is largely - copied from https://github.com/Azure/azure-quickstart-templates/blob/master/test/ci-scripts/Kill-AzResourceGroup.ps1. - -.PARAMETER ResourceGroupName - The name of the resource group to remove. -#> -param( - [string][Parameter(mandatory = $true)] $ResourceGroupName -) - -Import-Module .\scripts\azure-pipelines\utils\AzurePipelinesUtils.psm1 -Force -Import-Module .\scripts\azure-pipelines\utils\AzureResourceUtils.psm1 -Force - - -Invoke-AzurePipelinesTask { - Write-Host "Removing resource group:" $ResourceGroupName "..." - - # First, try removing and purging soft-delete enabled resources... - Write-Host "Removing soft-delete enabled resources..." - - Write-Host "Checking for Key vaults..." - Remove-AzKeyVaultInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Cognitive Services accounts..." - Remove-AzCognitiveServicesAccountInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for API Management services..." - Remove-AzApiManagementServiceInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Operational Insights workspaces..." - Remove-AzOperationalInsightsWorkspaceInResourceGroup -ResourceGroupName $ResourceGroupName - - # Try removing the resource group. - if (Remove-AzResourceGroup -Name $ResourceGroupName -Force -Verbose -ErrorAction "SilentlyContinue") { - exit - } - - # Failed to remove the resource group - try cleaning up resources that can prevent removing resource groups. - Write-Host "Failed to remove the resource group. Try cleaning up resources first..." - - Write-Host "Checking for resource locks..." - Remove-AzResourceLockInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Recovery Services vaults..." - Remove-AzRecoveryServicesVaultInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Backup vaults..." - Remove-AzDataProtectionBackupVaultInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Event Hubs Geo-disaster recovery configurations..." - Remove-AzEventHubGeoDRConfigurationInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Service Bus Hubs Geo-disaster recovery configurations and migrations..." - Remove-AzServiceBusGeoDRConfigurationAndMigrationInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Web App Swift Virtual Network connections..." - Remove-AzWebAppSwiftVnetConnectionInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Redis Cache geo-replication links..." - Remove-AzRedisCacheLinkInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Subnet delegations..." - Remove-AzSubnetDelegationInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Virtual Hub IP configurations..." - Remove-AzVirtualHubIPConfigurationInResourceGroup -ResourceGroupName $ResourceGroupName - - Write-Host "Checking for Private Endpoint connections..." - Remove-AzPrivateEndpointConnectionInResourceGroup -ResourceGroupName $ResourceGroupName - - # Finally... - Write-Host "Removed resources preventing removing the resource group. Attempting to remove the resource group again..." - Remove-AzResourceGroup -Force -Verbose -Name $ResourceGroupName -} diff --git a/scripts/azure-pipelines/utils/AzurePipelinesUtils.psm1 b/scripts/azure-pipelines/utils/AzurePipelinesUtils.psm1 deleted file mode 100644 index e9186dd953..0000000000 --- a/scripts/azure-pipelines/utils/AzurePipelinesUtils.psm1 +++ /dev/null @@ -1,33 +0,0 @@ -function Invoke-AzurePipelinesTask { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [ScriptBlock]$ScriptBlock - ) - - try { - $ScriptBlock.Invoke(); - } - catch { - Write-Host "##vso[task.logissue type=error;]An error occurred: $_" - Write-Output "##vso[task.complete result=Failed;]" - } - - Exit $LASTEXITCODE -} - -function Set-AzurePipelinesVariable { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$VariableName, - - [Parameter(mandatory = $true)] - [AllowEmptyString()] - [string]$VariableValue - ) - - Write-Host "##vso[task.setvariable variable=$VariableName;]$VariableValue" -} - -Export-ModuleMember -Function Invoke-AzurePipelinesTask, Set-AzurePipelinesVariable, Set-AzurePipelinesOutputVariable diff --git a/scripts/azure-pipelines/utils/AzureResourceUtils.psm1 b/scripts/azure-pipelines/utils/AzureResourceUtils.psm1 deleted file mode 100644 index 568b9e9fd3..0000000000 --- a/scripts/azure-pipelines/utils/AzureResourceUtils.psm1 +++ /dev/null @@ -1,596 +0,0 @@ -function Wait-Replication { - [CmdletBinding()] - Param( - [Parameter(mandatory = $true)] - [scriptblock]$ScriptBlock, - - [int]$SuccessCount = 2, - - [int]$DelayInSeconds = 2, - - [int]$MaximumFailureCount = 20 - ) - - Begin { - $successiveSuccessCount = 0 - $failureCount = 0 - } - - Process { - while ($successiveSuccessCount -lt $SuccessCount) { - if ($ScriptBlock.Invoke()) { - $successiveSuccessCount++ - } - else { - $successiveSuccessCount = 0 - $failureCount++ - - if ($failureCount -eq $MaximumFailureCount) { - throw "Reached maximum failure count: $MaximumFailureCount." - } - } - } - - Start-Sleep $DelayInSeconds - } -} - -<# -.SYNOPSIS - Clears a Recovery Services vault. - -.DESCRIPTION - The function removes all backup items and configuration in a Recovery Services vault so it can be removed. - The source code was copied from https://docs.microsoft.com/en-us/azure/backup/scripts/delete-recovery-services-vault. - -.PARAMETER Vault - The vault to clear. -#> -function Clear-AzRecoveryServicesVault { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true, ValueFromPipeline = $true)] - [Microsoft.Azure.Commands.RecoveryServices.ARSVault]$Vault - ) - - process { - Write-Host "Clearing Recovery Services vault" $vault.Name "..." - - Set-AzRecoveryServicesAsrVaultContext -Vault $Vault - Set-AzRecoveryServicesVaultProperty -Vault $Vault.ID -SoftDeleteFeatureState Disable #disable soft delete - Write-Host "Soft delete disabled for the vault" $Vault.Name - - $containerSoftDelete = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM -WorkloadType AzureVM -VaultId $Vault.ID | Where-Object { $_.DeleteState -eq "ToBeDeleted" } #fetch backup items in soft delete state - foreach ($softitem in $containerSoftDelete) { - Undo-AzRecoveryServicesBackupItemDeletion -Item $softitem -VaultId $Vault.ID -Force #undelete items in soft delete state - } - #Invoking API to disable enhanced security - $body = @{properties = @{enhancedSecurityState = "Disabled" } } - $vaultPath = $Vault.ID + "/backupconfig/vaultconfig?api-version=2020-05-13" - - Invoke-AzRestMethod -Method "PATCH" -Path $vaultPath -Payload ($body | ConvertTo-JSON -Depth 5) - - #Fetch all protected items and servers - $backupItemsVM = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM -WorkloadType AzureVM -VaultId $Vault.ID - $backupItemsSQL = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureWorkload -WorkloadType MSSQL -VaultId $Vault.ID - $backupItemsAFS = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureStorage -WorkloadType AzureFiles -VaultId $Vault.ID - $backupItemsSAP = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureWorkload -WorkloadType SAPHanaDatabase -VaultId $Vault.ID - $backupContainersSQL = Get-AzRecoveryServicesBackupContainer -ContainerType AzureVMAppContainer -Status Registered -VaultId $Vault.ID | Where-Object { $_.ExtendedInfo.WorkloadType -eq "SQL" } - $protectableItemsSQL = Get-AzRecoveryServicesBackupProtectableItem -WorkloadType MSSQL -VaultId $Vault.ID | Where-Object { $_.IsAutoProtected -eq $true } - $backupContainersSAP = Get-AzRecoveryServicesBackupContainer -ContainerType AzureVMAppContainer -Status Registered -VaultId $Vault.ID | Where-Object { $_.ExtendedInfo.WorkloadType -eq "SAPHana" } - $StorageAccounts = Get-AzRecoveryServicesBackupContainer -ContainerType AzureStorage -Status Registered -VaultId $Vault.ID - $backupServersMARS = Get-AzRecoveryServicesBackupContainer -ContainerType "Windows" -BackupManagementType MAB -VaultId $Vault.ID - $backupServersMABS = Get-AzRecoveryServicesBackupManagementServer -VaultId $Vault.ID | Where-Object { $_.BackupManagementType -eq "AzureBackupServer" } - $backupServersDPM = Get-AzRecoveryServicesBackupManagementServer -VaultId $Vault.ID | Where-Object { $_.BackupManagementType -eq "SCDPM" } - $pvtendpoints = Get-AzPrivateEndpointConnection -PrivateLinkResourceId $Vault.ID - - foreach ($item in $backupItemsVM) { - Disable-AzRecoveryServicesBackupProtection -Item $item -VaultId $Vault.ID -RemoveRecoveryPoints -Force #stop backup and delete Azure VM backup items - } - Write-Host "Disabled and deleted Azure VM backup items" - - foreach ($item in $backupItemsSQL) { - Disable-AzRecoveryServicesBackupProtection -Item $item -VaultId $Vault.ID -RemoveRecoveryPoints -Force #stop backup and delete SQL Server in Azure VM backup items - } - Write-Host "Disabled and deleted SQL Server backup items" - - foreach ($item in $protectableItemsSQL) { - Disable-AzRecoveryServicesBackupAutoProtection -BackupManagementType AzureWorkload -WorkloadType MSSQL -InputItem $item -VaultId $Vault.ID #disable auto-protection for SQL - } - Write-Host "Disabled auto-protection and deleted SQL protectable items" - - foreach ($item in $backupContainersSQL) { - Unregister-AzRecoveryServicesBackupContainer -Container $item -Force -VaultId $Vault.ID #unregister SQL Server in Azure VM protected server - } - Write-Host "Deleted SQL Servers in Azure VM containers" - - foreach ($item in $backupItemsSAP) { - Disable-AzRecoveryServicesBackupProtection -Item $item -VaultId $Vault.ID -RemoveRecoveryPoints -Force #stop backup and delete SAP HANA in Azure VM backup items - } - Write-Host "Disabled and deleted SAP HANA backup items" - - foreach ($item in $backupContainersSAP) { - Unregister-AzRecoveryServicesBackupContainer -Container $item -Force -VaultId $Vault.ID #unregister SAP HANA in Azure VM protected server - } - Write-Host "Deleted SAP HANA in Azure VM containers" - - foreach ($item in $backupItemsAFS) { - Disable-AzRecoveryServicesBackupProtection -Item $item -VaultId $Vault.ID -RemoveRecoveryPoints -Force #stop backup and delete Azure File Shares backup items - } - Write-Host "Disabled and deleted Azure File Share backups" - - foreach ($item in $StorageAccounts) { - Unregister-AzRecoveryServicesBackupContainer -container $item -Force -VaultId $Vault.ID #unregister storage accounts - } - Write-Host "Unregistered Storage Accounts" - - foreach ($item in $backupServersMARS) { - Unregister-AzRecoveryServicesBackupContainer -Container $item -Force -VaultId $Vault.ID #unregister MARS servers and delete corresponding backup items - } - Write-Host "Deleted MARS Servers" - - foreach ($item in $backupServersMABS) { - Unregister-AzRecoveryServicesBackupManagementServer -AzureRmBackupManagementServer $item -VaultId $Vault.ID #unregister MABS servers and delete corresponding backup items - } - Write-Host "Deleted MAB Servers" - - foreach ($item in $backupServersDPM) { - Unregister-AzRecoveryServicesBackupManagementServer -AzureRmBackupManagementServer $item -VaultId $Vault.ID #unregister DPM servers and delete corresponding backup items - } - Write-Host "Deleted DPM Servers" - - #Deletion of ASR Items - - $fabricObjects = Get-AzRecoveryServicesAsrFabric - if ($null -ne $fabricObjects) { - # First DisableDR all VMs. - foreach ($fabricObject in $fabricObjects) { - $containerObjects = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $fabricObject - foreach ($containerObject in $containerObjects) { - $protectedItems = Get-AzRecoveryServicesAsrReplicationProtectedItem -ProtectionContainer $containerObject - # DisableDR all protected items - foreach ($protectedItem in $protectedItems) { - Write-Host "Triggering DisableDR(Purge) for item:" $protectedItem.Name - Remove-AzRecoveryServicesAsrReplicationProtectedItem -InputObject $protectedItem -Force - Write-Host "DisableDR(Purge) completed" - } - - $containerMappings = Get-AzRecoveryServicesAsrProtectionContainerMapping ` - -ProtectionContainer $containerObject - # Remove all Container Mappings - foreach ($containerMapping in $containerMappings) { - Write-Host "Triggering Remove Container Mapping: " $containerMapping.Name - Remove-AzRecoveryServicesAsrProtectionContainerMapping -ProtectionContainerMapping $containerMapping -Force - Write-Host "Removed Container Mapping." - } - } - $NetworkObjects = Get-AzRecoveryServicesAsrNetwork -Fabric $fabricObject - foreach ($networkObject in $NetworkObjects) { - #Get the PrimaryNetwork - $PrimaryNetwork = Get-AzRecoveryServicesAsrNetwork -Fabric $fabricObject -FriendlyName $networkObject - $NetworkMappings = Get-AzRecoveryServicesAsrNetworkMapping -Network $PrimaryNetwork - foreach ($networkMappingObject in $NetworkMappings) { - #Get the Neetwork Mappings - $NetworkMapping = Get-AzRecoveryServicesAsrNetworkMapping -Name $networkMappingObject.Name -Network $PrimaryNetwork - Remove-AzRecoveryServicesAsrNetworkMapping -InputObject $NetworkMapping - } - } - # Remove Fabric - Write-Host "Triggering Remove Fabric:" $fabricObject.FriendlyName - Remove-AzRecoveryServicesAsrFabric -InputObject $fabricObject -Force - Write-Host "Removed Fabric." - } - } - - foreach ($item in $pvtendpoints) { - $penamesplit = $item.Name.Split(".") - $pename = $penamesplit[1] - Remove-AzPrivateEndpointConnection -ResourceId $item.PrivateEndpoint.Id -Force #remove private endpoint connections - Remove-AzPrivateEndpoint -Name $pename -ResourceGroupName $Vault.ResourceGroupName -Force #remove private endpoints - } - Write-Host "Removed Private Endpoints" - - #Recheck ASR items in vault - $fabricCount = 1 - $ASRProtectedItems = 1 - $ASRPolicyMappings = 1 - $fabricObjects = Get-AzRecoveryServicesAsrFabric - if ($null -ne $fabricObjects) { - foreach ($fabricObject in $fabricObjects) { - $containerObjects = Get-AzRecoveryServicesAsrProtectionContainer -Fabric $fabricObject - foreach ($containerObject in $containerObjects) { - $protectedItems = Get-AzRecoveryServicesAsrReplicationProtectedItem -ProtectionContainer $containerObject - foreach ($protectedItem in $protectedItems) { - $ASRProtectedItems++ - } - $containerMappings = Get-AzRecoveryServicesAsrProtectionContainerMapping ` - -ProtectionContainer $containerObject - foreach ($containerMapping in $containerMappings) { - $ASRPolicyMappings++ - } - } - $fabricCount++ - } - } - #Recheck presence of backup items in vault - $backupItemsVMFin = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureVM -WorkloadType AzureVM -VaultId $Vault.ID - $backupItemsSQLFin = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureWorkload -WorkloadType MSSQL -VaultId $Vault.ID - $backupContainersSQLFin = Get-AzRecoveryServicesBackupContainer -ContainerType AzureVMAppContainer -Status Registered -VaultId $Vault.ID | Where-Object { $_.ExtendedInfo.WorkloadType -eq "SQL" } - $protectableItemsSQLFin = Get-AzRecoveryServicesBackupProtectableItem -WorkloadType MSSQL -VaultId $Vault.ID | Where-Object { $_.IsAutoProtected -eq $true } - $backupItemsSAPFin = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureWorkload -WorkloadType SAPHanaDatabase -VaultId $Vault.ID - $backupContainersSAPFin = Get-AzRecoveryServicesBackupContainer -ContainerType AzureVMAppContainer -Status Registered -VaultId $Vault.ID | Where-Object { $_.ExtendedInfo.WorkloadType -eq "SAPHana" } - $backupItemsAFSFin = Get-AzRecoveryServicesBackupItem -BackupManagementType AzureStorage -WorkloadType AzureFiles -VaultId $Vault.ID - $StorageAccountsFin = Get-AzRecoveryServicesBackupContainer -ContainerType AzureStorage -Status Registered -VaultId $Vault.ID - $backupServersMARSFin = Get-AzRecoveryServicesBackupContainer -ContainerType "Windows" -BackupManagementType MAB -VaultId $Vault.ID - $backupServersMABSFin = Get-AzRecoveryServicesBackupManagementServer -VaultId $Vault.ID | Where-Object { $_.BackupManagementType -eq "AzureBackupServer" } - $backupServersDPMFin = Get-AzRecoveryServicesBackupManagementServer -VaultId $Vault.ID | Where-Object { $_.BackupManagementType -eq "SCDPM" } - $pvtendpointsFin = Get-AzPrivateEndpointConnection -PrivateLinkResourceId $Vault.ID - Write-Host "Number of backup items left in the vault and which need to be deleted:" $backupItemsVMFin.count "Azure VMs" $backupItemsSQLFin.count "SQL Server Backup Items" $backupContainersSQLFin.count "SQL Server Backup Containers" $protectableItemsSQLFin.count "SQL Server Instances" $backupItemsSAPFin.count "SAP HANA backup items" $backupContainersSAPFin.count "SAP HANA Backup Containers" $backupItemsAFSFin.count "Azure File Shares" $StorageAccountsFin.count "Storage Accounts" $backupServersMARSFin.count "MARS Servers" $backupServersMABSFin.count "MAB Servers" $backupServersDPMFin.count "DPM Servers" $pvtendpointsFin.count "Private endpoints" - Write-Host "Number of ASR items left in the vault and which need to be deleted:" $ASRProtectedItems "ASR protected items" $ASRPolicyMappings "ASR policy mappings" $fabricCount "ASR Fabrics" $pvtendpointsFin.count "Private endpoints. Warning: This script will only remove the replication configuration from Azure Site Recovery and not from the source. Please cleanup the source manually. Visit https://go.microsoft.com/fwlink/?linkid=2182782 to learn more" - - $Vault - } -} - -function Remove-AzResourceLockInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - Get-AzResourceLock -ResourceGroupName $ResourceGroupName -Verbose | Remove-AzResourceLock -Force -verbose -} - -function Remove-AzRecoveryServicesVaultInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - Get-AzRecoveryServicesVault -ResourceGroupName $ResourceGroupName -Verbose | - Clear-AzRecoveryServicesVault | - Remove-AzRecoveryServicesVault -Verbose -} - -function Remove-AzDataProtectionBackupVaultInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - # The Az.DataProtection is not yet included with the rest of the Az module. - if ($null -eq $(Get-Module -ListAvailable Az.DataProtection)) { - Write-Host "Installing Az.DataProtection module..." - Install-Module Az.DataProtection -Force -AllowClobber - } - - $vaults = Get-AzDataProtectionBackupVault -ResourceGroupName $ResourceGroupName - - foreach ($vault in $vaults) { - Write-Host "Removing Backup vault" $vault.name "..." - - $backupInstances = Get-AzDataProtectionBackupInstance -ResourceGroupName $ResourceGroupName -VaultName $vault.Name - foreach ($backupInstance in $backupInstances) { - Write-Host "Removing Backup instance" $backupInstance.Name "..." - Remove-AzDataProtectionBackupInstance -ResourceGroupName $ResourceGroupName -VaultName $vault.Name -Name $backupInstance.Name - } - - $backupPolicies = Get-AzDataProtectionBackupPolicy -ResourceGroupName $ResourceGroupName -VaultName $vault.Name - foreach ($backupPolicy in $backupPolicies) { - Write-Host "Removing Backup policy" $backupPolicy.name "..." - Remove-AzDataProtectionBackupPolicy -ResourceGroupName $ResourceGroupName -VaultName $vault.name -Name $backupPolicy.Name - } - } -} - -function Remove-AzEventHubGeoDRConfigurationInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $namespaces = Get-AzEventHubNamespace -ResourceGroupName $ResourceGroupName -Verbose - - foreach ($namespace in $namespaces) { - $configurations = Get-AzEventHubGeoDRConfiguration -ResourceGroupName $ResourceGroupName -Namespace $namespace.Name -Verbose - - foreach ($configuration in $configurations) { - # First look at the primary namespaces and break pairing. - if ($configuration.Role.ToString() -eq "Primary") { - Write-Host "Breaking Event Hubs namespace pairing for namespace" $namespace.Name "..." - Set-AzEventHubGeoDRConfigurationBreakPair -ResourceGroupName $ResourceGroupName -Namespace $namespace.Name -Name $configuration.Name - } - - # Now that pairing is removed we can remove primary and secondary configs. - Write-Host "Removing Event Hubs DR configuration" $configuration.Name "..." - Remove-AzEventHubGeoDRConfiguration -ResourceGroupName $ResourceGroupName -Namespace $namespace.Name $configuration.Name -Verbose - } - } -} - -function Remove-AzServiceBusGeoDRConfigurationAndMigrationInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $namespaces = Get-AzServiceBusNamespace -ResourceGroupName $ResourceGroupName -Verbose - - foreach ($namespace in $namespaces) { - $configurations = Get-AzServiceBusGeoDRConfiguration -ResourceGroupName $ResourceGroupName -Namespace $namespace.Name - - foreach ($configuration in $configurations) { - # First look at the primary namespaces and break pairing. - if ($configuration.Role.ToString() -eq "Primary") { - Write-Host "Breaking Service Bus namespace pairing for namespace" $namespace "..." - Set-AzServiceBusGeoDRConfigurationBreakPair -ResourceGroupName $ResourceGroupName -Namespace $namespace.Name -Name $configuration.Name - } - - # Now that pairing is removed we can remove primary and secondary configs. - Write-Host "Removing Service Bus DR configuration" $configuration.Name "..." - Remove-AzServiceBusGeoDRConfiguration -ResourceGroupName $ResourceGroupName -Namespace $namespace.Name -Name $configuration.Name -Verbose - } - } - - foreach ($namespace in $namespaces) { - # Set ErrorAction on this since it throws if there is no config (unlike the other cmdlets). - $migration = Get-AzServiceBusMigration -ResourceGroupName $ResourceGroupName -Name $namespace.Name -ErrorAction "SilentlyContinue" - - if ($migration) { - Write-Host "Removing Service Bus migration" $migration.Name "..." - Remove-AzServiceBusMigration -ResourceGroupName $ResourceGroupName -Name $namespace.Name -Verbose - } - } -} - -function Remove-AzWebAppSwiftVnetConnectionInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - function Remove-AzWebAppSwiftVnetConnection { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [Microsoft.Azure.Commands.WebApps.Models.PSSite]$WebAppOrSlot - ) - - process { - # Assumption is that there can only be one connection, but it returns an array so maybe not. - $result = Invoke-AzRestMethod -Method "GET" -Path "$($WebAppOrSlot.Id)/virtualNetworkConnections?api-version=2021-10-01" - - if ($result.StatusCode -eq "200") { - Write-Host "Removing a Swift Virtual Network connection from the Web App: $($WebAppOrSlot.Name)" - # The URI for remove is not the same as the GET URI. - Invoke-AzRestMethod -Method "DELETE" -Path "$($WebAppOrSlot.Id)/networkConfig/virtualNetwork?api-version=2021-10-01" -Verbose - } - } - } - - # Web apps that have a serviceAssociationLink can be deleted even if the link exists and the vnet - # will be bricked (cannot be delete and the serviceAssociation link cannot be removed). - # A funky traversal of 4 resources are needed to discover and remove the link (PUT/GET/DELETE are not symmetrical). - $webApps = Get-AzWebApp -ResourceGroupName $ResourceGroupName -Verbose - - foreach ($webApp in $webApps) { - # Remove the config on the WebApp slots. - Get-AzWebAppSlot -ResourceGroupName $ResourceGroupName -Name $webApp.Name -Verbose | Remove-AzWebAppSwiftVnetConnection - - # Now remove the config on the WebApp itself. - Remove-AzWebAppSwiftVnetConnection -WebAppOrSlot $webApp - } -} - -function Remove-AzRedisCacheLinkInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $redisCaches = Get-AzRedisCache -ResourceGroupName $ResourceGroupName -Verbose - - foreach ($redisCache in $redisCaches) { - $link = Get-AzRedisCacheLink -Name $redisCache.Name - - if ($link) { - Write-Host "Removing Redis Cache geo-replication link" $link.Name "..." - $link | Remove-AzRedisCacheLink -Verbose - } - } -} - -function Remove-AzSubnetDelegationInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - # ACI create a subnet delegation that must be removed before the vnet can be deleted. - $vnets = Get-AzVirtualNetwork -ResourceGroupName $ResourceGroupName -Verbose - - foreach ($vnet in $vnets) { - foreach ($subnet in $vnet.Subnets) { - $delegations = Get-AzDelegation -Subnet $subnet -Verbose - - foreach ($delegation in $delegations) { - Write-Output "Removing Subnet delegation" $delegation.Name "..." - Remove-AzDelegation -Name $delegation.Name -Subnet $subnet -Verbose - } - } - } -} - -function Remove-AzVirtualHubIPConfigurationInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - # Virtual Hubs can have ipConfigurations that take a few minutes to delete. - # There appears to be no cmdlets or CLI to invoke these APIs. - $virtualHubs = Get-AzVirtualHub -ResourceGroupName $ResourceGroupName -Verbose - - foreach ($virtualHub in $virtualHubs) { - $listResult = Invoke-AzRestMethod -Method "GET" -path "$($virtualHub.Id)/ipConfigurations?api-version=2020-11-01" - $configurations = $($listResult.Content | ConvertFrom-Json -Depth 50).Value - - foreach ($configuration in $configurations) { - Write-Host "Removing Virtual Hub IP configuration" $($configuration.id) "..." - - Write-Host "Sending a DELETE request..." - $deleteResult = Invoke-AzRestMethod -Method "DELETE" -Path "$($configuration.id)?api-version=2020-11-01" - $deleteResult - - if ($deleteResult.StatusCode -like "20*") { - Write-Host "Waiting for the DELETE operation to complte..." - do { - Start-Sleep -Seconds 60 - Write-Host "Making sure GET returns 404..." - $getResult = Invoke-AzRestMethod -Method GET -Path "$($configuration.id)?api-version=2020-11-01" - $getResult - } until ($getResult.StatusCode -eq "404") - } - } - } -} - -function Remove-AzPrivateEndpointConnectionInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $privateLinkServices = Get-AzPrivateLinkService -ResourceGroupName $ResourceGroupName - - foreach ($privateLinkService in $privateLinkServices) { - $connections = Get-AzPrivateEndpointConnection -ResourceGroupName $ResourceGroupName -ServiceName $privateLinkService.Name - - foreach ($connection in $connections) { - Write-Host "Removing Private Endpoint connection" $connection.Name "..." - Remove-AzPrivateEndpointConnection -ResourceGroupName $ResourceGroupName -ServiceName $privateLinkService.Name -Name $connection.Name -Force - } - } -} - -function Remove-AzKeyVaultInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $keyVaults = Get-AzKeyVault -ResourceGroupName $ResourceGroupName - - foreach ($keyVault in $keyVaults) { - Write-Host "Removing Key vault" $keyVault.VaultName "..." - Remove-AzKeyVault -VaultName $keyVault.VaultName -ResourceGroupName $ResourceGroupName -Force - - if (-not $keyVault.EnableSoftDelete) { - continue - } - - if ($keyVault.EnablePurgeProtection) { - Write-Warning ('Key vault {0} had purge protection enabled. The retention time is {1} days. Please wait until after this period before re-running the test.' -f $keyVault.VaultName, $keyVault.SoftDeleteRetentionInDays) - } - else { - Wait-Replication { - Write-Host "Waiting for the Key vault deletion operation to complete..." - $null -ne (Get-AzKeyVault -VaultName $keyVault.VaultName -Location $keyVault.Location -InRemovedState) - } - - Write-Host "Purging Key vault" $keyVault.VaultName "..." - Remove-AzKeyVault -VaultName $keyVault.VaultName -Location $keyVault.Location -InRemovedState -Force - } - } -} - -function Remove-AzCognitiveServicesAccountInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $accounts = Get-AzCognitiveServicesAccount -ResourceGroupName $ResourceGroupName - - foreach ($account in $accounts) { - Write-Host "Removing Cognitive Services account" $account.AccountName "..." - $account | Remove-AzCognitiveServicesAccount -Force - - Wait-Replication { - Write-Host "Waiting for the Cognitive Services account deletion operation to complete..." - $null -ne (Get-AzCognitiveServicesAccount -ResourceGroupName $ResourceGroupName -Name $account.AccountName -Location $account.Location -InRemovedState) - } - - Write-Host "Purging Cognitive Services account" $account.AccountName "..." - $account | Remove-AzCognitiveServicesAccount -Location $account.Location -InRemovedState -Force - } -} - -function Remove-AzApiManagementServiceInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $context = Get-AzContext - $subscriptionId = $context.Subscription.Id - $services = Get-AzApiManagement -ResourceGroupName $ResourceGroupName - - foreach ($service in $services) { - Write-Host "Removing API Management service" $service.Name "..." - $service | Remove-AzApiManagement - - Write-Host "Waiting for the API Management service deletion operation to complete..." - Start-Sleep 20 - - Write-Host "Purging API Management service" $service.Name "..." - $purgePath = "/subscriptions/{0}/providers/Microsoft.ApiManagement/locations/{1}/deletedservices/{2}?api-version=2020-06-01-preview" -f $subscriptionId, $service.Location, $service.Name - Invoke-AzRestMethod -Method "DELETE" -Path $purgePath - } -} - -function Remove-AzOperationalInsightsWorkspaceInResourceGroup { - [CmdletBinding()] - param ( - [Parameter(mandatory = $true)] - [string]$ResourceGroupName - ) - - $workspaces = Get-AzOperationalInsightsWorkspace -ResourceGroupName $ResourceGroupName - - foreach ($workspace in $workspaces) { - Write-Host "Removing Operational Insights workspace" $workspace.Name "..." - $workspace | Remove-AzOperationalInsightsWorkspace -ForceDelete -Force - } -} - -Export-ModuleMember -Function ` - Wait-Replication, ` - Remove-AzResourceLockInResourceGroup, ` - Remove-AzRecoveryServicesVaultInResourceGroup, ` - Remove-AzDataProtectionBackupVaultInResourceGroup, ` - Remove-AzEventHubGeoDRConfigurationInResourceGroup, ` - Remove-AzServiceBusGeoDRConfigurationAndMigrationInResourceGroup, ` - Remove-AzWebAppSwiftVnetConnectionInResourceGroup, ` - Remove-AzRedisCacheLinkInResourceGroup, ` - Remove-AzSubnetDelegationInResourceGroup, ` - Remove-AzVirtualHubIPConfigurationInResourceGroup, ` - Remove-AzPrivateEndpointConnectionInResourceGroup, ` - Remove-AzKeyVaultInResourceGroup, ` - Remove-AzCognitiveServicesAccountInResourceGroup, ` - Remove-AzApiManagementServiceInResourceGroup, ` - Remove-AzOperationalInsightsWorkspaceInResourceGroup - \ No newline at end of file diff --git a/scripts/github-actions/create-tag.js b/scripts/github-actions/create-tag.js deleted file mode 100644 index fee3b918e9..0000000000 --- a/scripts/github-actions/create-tag.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @typedef Params - * @property {typeof require} require - * @property {ReturnType} github - * @property {typeof import("@actions/github").context} context - * @property {typeof import("@actions/core")} core - * @property {string} moduleDir - * @property {string} baseVersion - * @property {string} headVersion - * - * @param {Params} params - */ -async function createTag({ - require, - github, - context, - core, - moduleDir, - baseVersion, - headVersion, -}) { - const semverCompare = require("semver/functions/compare"); - const base = context.payload.before; - const head = context.payload.after; - const compareResult = semverCompare(headVersion, baseVersion); - - if (compareResult < 0) { - core.setFailed( - `The version ${headVersion} calculated at the commit ${head} (head) is smaller than the version ${baseVersion} calculated at the base commit ${base} (base).` - ); - } - - if (compareResult === 0) { - core.info(`No version update detected.`); - return ""; - } - - const red = "\u001b[31m"; - const green = "\u001b[32m"; - const reset = "\u001b[0m"; - core.info( - `Detected version update: ${red}${baseVersion} (old) ${reset}-> ${green}${headVersion} (new).` - ); - - const modulePath = moduleDir.substring(moduleDir.indexOf("/") + 1); - const tag = `${modulePath}/${headVersion}`; - - await github.rest.git.createRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: `refs/tags/${tag}`, - sha: context.sha, - }); - - core.info(`Created a new tag: ${tag} (${head}).`); - return tag; -} - -module.exports = createTag; diff --git a/scripts/github-actions/get-changed-module.js b/scripts/github-actions/get-changed-module.js deleted file mode 100644 index 479cb9bdd4..0000000000 --- a/scripts/github-actions/get-changed-module.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @typedef Params - * @property {typeof require} require - * @property {ReturnType} github - * @property {typeof import("@actions/github").context} context - * @property {typeof import("@actions/core")} core - * - * @param {Params} params - */ -async function getChangedModule({ require, github, context, core }) { - let base; - let head; - - switch (context.eventName) { - case "pull_request": - base = context.payload.pull_request.base.sha; - head = context.payload.pull_request.head.sha; - break; - case "push": - base = context.payload.before; - head = context.payload.after; - break; - default: - core.setFailed(`Not supported event: ${context.eventName}.`); - } - - const { status, data } = await github.rest.repos.compareCommitsWithBasehead({ - owner: context.repo.owner, - repo: context.repo.repo, - basehead: `${base}...${head}`, - }); - - if (status !== 200) { - core.setFailed( - `Expected github.rest.repos.compareCommitsWithBasehead to return 200, got ${status}.` - ); - } - - if (context.eventName === "push" && data.status !== "ahead") { - core.setFailed( - `The head commit ${head} is not ahead of the base commit ${base}.` - ); - } - - const path = require("path"); - const fs = require("fs"); - const cyan = "\u001b[36;1m"; - - const moduleDirs = [ - ...new Set( - data.files - .filter((file) => file.filename.startsWith("modules/")) - // Do not consider module changed if only the README.md has changed - .filter((file) => !file.filename.endsWith("README.md")) - .map((file) => { - const dir = path.dirname(file.filename); - const segments = dir.split("/"); - // modules/moduleFolder/moduleRoot/* => modules/moduleFolder/moduleRoot - return segments.slice(0, 3).join("/"); - }) - // Ignore removed module directories. - .filter((dir) => fs.existsSync(dir)) - ), - ]; - - switch (moduleDirs.length) { - case 0: - core.info("No changed module found."); - return ""; - case 1: - core.info("Found 1 changed module:"); - core.info(`- ${cyan}${moduleDirs[0]}`); - return moduleDirs[0]; - default: - core.info(`Found ${moduleDirs.length} changed modules:`); - moduleDirs.forEach((dir) => core.info(`- ${cyan}${dir}`)); - core.setFailed("Only one module can be added or updated at a time."); - } -} - -module.exports = getChangedModule; diff --git a/scripts/github-actions/parse-tag.js b/scripts/github-actions/parse-tag.js deleted file mode 100644 index 99f86b1d61..0000000000 --- a/scripts/github-actions/parse-tag.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @typedef Params - * @property {typeof import("@actions/core")} core - * @property {string} tag - * - * @param {Params} params - */ -function parseTag({ core, tag }) { - const segments = tag.split("/"); - - if (segments.length !== 3 || segments.includes("")) { - core.setFailed( - `Invalid tag: "${tag}". A valid tag must be in the format of "//".` - ); - } - - const modulePathSegmentRegex = /^[a-z0-9]+([._-][a-z0-9]+)*$/; - const moduleFolder = segments[0]; - const moduleName = segments[1]; - - if (!modulePathSegmentRegex.test(moduleFolder)) { - core.setFailed( - `The module folder "${moduleFolder}" in the tag "${tag}" is invalid. It must match the regex "${modulePathSegmentRegex}".` - ); - } - - if (!modulePathSegmentRegex.test(moduleName)) { - core.setFailed( - `The module name "${moduleName}" in the tag "${tag}" is invalid. It must match the regex "${modulePathSegmentRegex}".` - ); - } - - const versionRegex = - /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$/; - const version = segments[2]; - - if (!versionRegex.test(version)) { - core.setFailed( - `The version "${version}" in the tag "${tag}" is invalid. It must match the regex "${versionRegex}".` - ); - } - - core.setOutput("module_path", `${moduleFolder}/${moduleName}`); - core.setOutput("version", version); - - const readmeLink = `https://github.com/Azure/bicep-registry-modules/tree/${tag}/modules/${moduleFolder}/${moduleName}/README.md`; - core.setOutput("documentation_uri", readmeLink); -} - -module.exports = parseTag; diff --git a/scripts/local/approve-autogenerated-prs.js b/scripts/local/approve-autogenerated-prs.js deleted file mode 100644 index 920d11ac1e..0000000000 --- a/scripts/local/approve-autogenerated-prs.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - - This script approves all auto-generated PRs. It will query interactively for each module. - - Usage: - Install gh from https://cli.github.com/ - Log in to gh - cd repos/bicep-registry-modules - npm i - node scripts/local/approve-autogenerated-prs.js - - - -*/ - -const { - runAsync, - queryUserAsync, - queryRunAsync, -} = require("./util/runAsync.js"); -const { clearScreen, green, yellow, red, reset } = require("./util/colors"); - -const { argv } = require("process"); - -function formatPR(pr) { - return `#${pr.number} (${pr.author.login} ${pr.state} ${pr.labels.map( - (l) => l.name - )}) ${yellow}${pr.title}${reset}`; -} - -async function ApprovePRs() { - const prs = JSON.parse( - await runAsync( - `gh pr list --label "Auto-generated" --state open --limit 500 --json number,author,title,state,labels,statusCheckRollup -S "review:required draft:false"` - ) - ); - - console.log(`${yellow}Found these PRs:${reset}`); - for (pr of prs) { - console.log(formatPR(pr)); - } - console.log(); - - for (pr of prs) { - await queryRunAsync( - [ - `gh pr merge --auto --squash ${pr.number}`, - `gh pr review --approve ${pr.number}`, - ], - `Approve the following PR?${reset}\n${formatPR(pr)}` - ); - } -} - -ApprovePRs(); diff --git a/scripts/local/create-prs.js b/scripts/local/create-prs.js deleted file mode 100644 index e448f40988..0000000000 --- a/scripts/local/create-prs.js +++ /dev/null @@ -1,102 +0,0 @@ -/* - - This script creates a PR for each modules on the local disk that has changes. It will query interactively for each module. - - Requires that gh be installed (https://cli.github.com/) - - Usage: - cd repos/bicep-registry-modules - npm i - - - - make sure you're logged in to gh - node scripts/local/create-prs.js - - - -*/ - -const usage = `Usage: see script comments`; - -const { runAsync, queryRunAsync } = require("./util/runAsync.js"); -const { clearScreen, green, yellow, red, reset } = require("./util/colors"); - -const { argv } = require("process"); - -// eslint-disable-next-line prefer-const -let [branchPrefix, commitPrefix] = argv.slice(2); -if (!branchPrefix || !commitPrefix) { - console.error(usage); - process.exit(1); -} - -branchPrefix = branchPrefix.endsWith("/") - ? branchPrefix.slice(0, branchPrefix.length - 1) - : branchPrefix; -console.log(`${green}branch prefix: ${branchPrefix}${reset}`); -console.log(`${green}commit message: ${commitPrefix}${reset}`); - -async function getChangedModulesAsync() { - const changedModules = (await runAsync(`git status modules`)) - .split("\n") - .filter((line) => line.match(/modified/)) - .map((line) => line.replace(/^.*\smodules\/([^/]+\/[^/]+)\/.*$/g, "$1")) - // dedupe the list - .filter((value, index, self) => self.indexOf(value) === index); - - return changedModules; -} - -async function CreatePRAsync(modulePath) { - console.log( - `${clearScreen}${green}=========== Creating PR for ${modulePath}...${reset}` - ); - - const branchName = `${branchPrefix}/auto/${modulePath}`; - await runAsync(`git checkout -b ${branchName}`); - await runAsync(`git add modules/${modulePath}`); - await runAsync(`git diff --cached`); - - const commitMessage = `${commitPrefix} (${modulePath}) (auto)`; - const prTitle = `${commitPrefix} (${modulePath}) (auto-generated)`; - const prBody = `This PR was created by a script. Please review the changes and merge if they look good.`; - const prSubmitted = await queryRunAsync([ - `git commit -m "${commitMessage}"`, - `git push -u origin ${branchName}`, - `gh pr create --title "${prTitle}" --body "${prBody}" --label "Auto-generated"`, - ]); - - // TODO: Changes get lost for a branch if you say no - - await runAsync(`git checkout main`); - if (prSubmitted) { - await runAsync(`git branch -d ${branchName}`); - } else { - console.log( - `${red}Branch ${branchName} contains the unsubmitted changes.${reset}` - ); - } - - console.log(); -} - -async function CreatePRs() { - const changedModules = await getChangedModulesAsync(); - - const currentBranch = await runAsync(`git symbolic-ref --short HEAD`, false); - console.log(`${green}Current branch: ${currentBranch}${reset}`); - - await runAsync(`git checkout main`); - - try { - for (const modulePath of changedModules) { - await CreatePRAsync(modulePath); - } - } finally { - await runAsync(`git checkout ${currentBranch}`, false); - console.log(`${green}Restored branch to ${currentBranch}${reset}`); - } -} - -CreatePRs(); diff --git a/scripts/local/fix-all-readme-examples.js b/scripts/local/fix-all-readme-examples.js deleted file mode 100644 index 742318ee09..0000000000 --- a/scripts/local/fix-all-readme-examples.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - - This script updates the examples in all README.md files to the *current* version. - - Usage: - cd repos/bicep-registry-modules - npm i - node scripts/local/fix-all-readme-examples.js - -*/ - -const { updateReadmeExamples } = require("./util/updateReadmeExamples.js"); -const { - getLatestModuleVersionsAsync, -} = require("./util/getLatestModuleVersionsAsync"); - -async function regenerateAllAsync() { - const modules = await getLatestModuleVersionsAsync(); - - for (const [modulePath, version] of modules) { - updateReadmeExamples(modulePath, version, version); - } -} - -regenerateAllAsync(); diff --git a/scripts/local/regenerate-all.js b/scripts/local/regenerate-all.js deleted file mode 100644 index aaa1630103..0000000000 --- a/scripts/local/regenerate-all.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - - This script is used to regenerate all the modules in the current repo. Also updates the examples - in the README.md to the *next* version of the module (making it ready for checkin). - - Usage: - cd repos/bicep-registry-modules - npm i - node scripts/local/regenerate-all.js - -*/ - -const { resolve } = require("path"); -const { updateReadmeExamples } = require("./util/updateReadmeExamples.js"); -const { runAsync } = require("./util/runAsync.js"); -const { existsSync } = require("fs"); -const { - getLatestModuleVersionsAsync, -} = require("./util/getLatestModuleVersionsAsync.js"); -const { green, reset, yellow } = require("./util/colors"); -const semver = require("semver"); - -let brm = resolve( - `../bicep/src/Bicep.RegistryModuleTool/Bin/Debug/net7.0/Azure.Bicep.RegistryModuleTool` -); -brm = existsSync(brm) ? brm : brm + ".exe"; -brm = existsSync(brm) ? brm : "brm"; - -console.warn(`Using this brm: ${brm}`); - -function getNextVersion(version) { - return semver.inc(version, "patch"); -} - -async function regenerateAllAsync() { - const modules = await getLatestModuleVersionsAsync(); - - for (const [modulePath, version] of modules) { - var currentDir = process.cwd(); - process.chdir(resolve(`modules/${modulePath}`)); - - try { - let needsGenerate = false; - - try { - console.warn(`${yellow}Validating: ${modulePath}${reset}`); - await runAsync(`${brm} validate`); - } catch (err) { - if ( - /Please run "brm generate"/.test(String(err)) || - /The "summary" property in metadata.json does not match/.test( - String(err) - ) - ) { - needsGenerate = true; - } else { - throw err; - } - } - - if (needsGenerate) { - console.error( - `${yellow}Generating: ${modulePath} (version ${version})${reset}` - ); - await runAsync(`${brm} generate`); - - console.error( - `${yellow}Updating README for ${modulePath} (version ${version})${reset}` - ); - process.chdir(resolve(currentDir)); - await updateReadmeExamples( - modulePath, - version, - getNextVersion(version) - ); - } else { - console.error( - `${green}Does not need regeneration: ${modulePath}${reset}` - ); - } - } finally { - process.chdir(resolve(currentDir)); - } - } -} - -regenerateAllAsync(); diff --git a/scripts/local/util/colors.js b/scripts/local/util/colors.js deleted file mode 100644 index ff60da628e..0000000000 --- a/scripts/local/util/colors.js +++ /dev/null @@ -1,6 +0,0 @@ -exports.red = "\u001b[31m"; -exports.green = "\u001b[32m"; -exports.blue = "\u001b[34m"; -exports.yellow = "\u001b[33m"; -exports.reset = "\u001b[0m"; -exports.clearScreen = "\u001b[2J\u001b[0;0H"; diff --git a/scripts/local/util/getLatestModuleVersionsAsync.js b/scripts/local/util/getLatestModuleVersionsAsync.js deleted file mode 100644 index b8309ac35b..0000000000 --- a/scripts/local/util/getLatestModuleVersionsAsync.js +++ /dev/null @@ -1,46 +0,0 @@ -const { runAsync } = require("./runAsync.js"); - -function parseTag(tag) { - const parseTagScript = require("../../github-actions/parse-tag.js"); - - let modulePath, version; - parseTagScript({ - core: { - setOutput: (key, value) => { - if (key === "version") { - version = value; - } else if (key === "module_path") { - modulePath = value; - } - }, - setFailed: (message) => { - throw new Error(message); - }, - }, - tag, - }); - - return { modulePath, version }; -} - -async function getLatestModuleVersionsAsync() { - const tags = (await runAsync(`git tag -l`, false)) - .split("\n") - .filter((tag) => tag !== ""); - const latestModules = new Map(); - for (const tag of tags) { - const { modulePath, version } = parseTag(tag); - if (!latestModules.has(modulePath)) { - latestModules.set(modulePath, version); - } else { - const currentVersion = latestModules.get(modulePath); - if (version > currentVersion) { - latestModules.set(modulePath, version); - } - } - } - - return Array.from(latestModules); -} - -exports.getLatestModuleVersionsAsync = getLatestModuleVersionsAsync; diff --git a/scripts/local/util/runAsync.js b/scripts/local/util/runAsync.js deleted file mode 100644 index b40a6d0540..0000000000 --- a/scripts/local/util/runAsync.js +++ /dev/null @@ -1,72 +0,0 @@ -let runAll = false; - -const { exec } = require("child_process"); -const { promisify } = require("util"); -const { yellow, red, reset } = require("./colors"); - -const execPromise = promisify(exec); - -async function runAsync(cmd, echo = true) { - if (echo) { - console.log(`${yellow}${cmd}${reset}`); - } - - const response = await execPromise(cmd, {}); - if (echo) { - console.log(`> ${response.stdout}`); - } - return response.stdout; -} - -function queryUserAsync(question) { - const readline = require("readline").createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise((resolve) => { - readline.question(question, (answer) => { - readline.close(); - resolve(answer); - }); - }); -} - -async function queryRunAsync(cmds, optionalFriendlyPrompt) { - cmds = typeof cmds === "string" ? [cmds] : cmds; - let run = runAll === true; - if (!run) { - const prompt = - `${red}` + - (optionalFriendlyPrompt ?? - `Run the following commands?\n${yellow}${cmds.join("\n")}${reset}?`); - const answer = await queryUserAsync( - `${reset}${prompt} ${red}(y/n/a/q)${reset}` - ); - console.log(`answer: ${answer}`); - - if (answer === "y") { - run = true; - } else if (answer === "a") { - runAll = true; - run = true; - } else if (answer === "q") { - throw new Error("User aborted"); - } - } - - if (run) { - for (const cmd of cmds) { - await runAsync(cmd); - } - - return true; - } else { - return false; - } -} - -exports.runAsync = runAsync; -exports.queryRunAsync = queryRunAsync; -exports.queryUserAsync = queryUserAsync; -exports.runAll = runAll; diff --git a/scripts/local/util/updateReadmeExamples.js b/scripts/local/util/updateReadmeExamples.js deleted file mode 100644 index 1f3bf20840..0000000000 --- a/scripts/local/util/updateReadmeExamples.js +++ /dev/null @@ -1,70 +0,0 @@ -const { readFileSync } = require("fs"); - -// Updates the module version references in the README.md file -function updateReadmeExamples( - modulePath, - currentVersion, // what the current version should be - updateToVersion // update all versions to this -) { - const fs = require("fs"); - const path = require("path"); - const red = "\u001b[31m"; - const green = "\u001b[32m"; - const blue = "\u001b[34m"; - const yellow = "\u001b[33m"; - const reset = "\u001b[0m"; - - console.log(`Updating README.md examples for ${modulePath}...`); - - const readmePath = path.join("./modules", modulePath, "README.md"); - const readmeContent = readFileSync(readmePath, "utf8"); - - const currentModulePath = `br/public:${modulePath}:${currentVersion}`; - const nextModulePath = `br/public:${modulePath}:${updateToVersion}`; - - const versionPattern = "[0-9.a-z]+"; - const anyRefPattern = `br/public:${modulePath}:${versionPattern}`; - - // Look for unexpected versions - let versions = Array.from(readmeContent.matchAll(anyRefPattern)); - versions = Array.from(new Set(versions.map((m) => m[0]))); // dedupe - if (versions.length === 0) { - console.warn( - `${yellow}No module references found in ${readmePath}.${reset}` - ); - return false; - } - - const unexpectedVersions = versions.filter( - (m) => m !== currentModulePath && m !== nextModulePath - ); - if (unexpectedVersions.length > 0) { - console.error( - `... ${red}UNEXPECTED VERSIONS FOUND in ${readmePath}: ${unexpectedVersions}${reset}` - ); - console.error( - currentModulePath == nextModulePath - ? `... Expected ${currentModulePath}` - : `... Expected either ${currentModulePath} or ${nextModulePath}${reset}` - ); - } - - const updatedReadmeContent = readmeContent.replace( - new RegExp(anyRefPattern, "g"), - nextModulePath - ); - if (updatedReadmeContent !== readmeContent) { - console.log( - `... ${green}Updated module references in README.md to ${nextModulePath}.${reset}` - ); - fs.writeFileSync(readmePath, updatedReadmeContent, "utf8"); - return true; - } else { - console.log( - `... ${blue}Module references in README.md are up-to-date.${reset}` - ); - return false; - } -} - -exports.updateReadmeExamples = updateReadmeExamples;