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 99% rename from .github/workflows/avm.platform.check.psrule.yml rename to .github/workflows/platform.check.psrule.yml index 6a61e8bacd..9c60895944 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: 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-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-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/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;