Skip to content

Azure-Pipelines

azure-pipelines.yml

This is the YAML File which defined the main Pipeline while this Docs your are reading have been deployed. Most Parts of it are referencing Templates which are located bellow the subfolder /azure-pipelines.

I had the Idea to use this folder for pipeline Stage-Templates, then use the azure-pipelines/jobs subfolder for job-templates and finally a subfolder azure-pipelines/jobs/steps for, you guessed it, step-templates. But at the moment I am thinking to have another subfolder-stage azure-pipelines/stages/jobs/steps, and store pipelines directly in azure-pipelines, which atm are stored in azure-pipelines/triggers which feels kind of wrong.

azure-pipelines.yml
trigger:
  batch: true
  branches:
    include:
      - refs/heads/main

pr:
  - main

resources:
  repositories:
    - repository: self
      endpoint: Mauwii

variables:
  - template: azure-pipelines/variables/default.yml
  - ${{ if in(variables['Build.SourceBranch'], 'refs/heads/main', 'refs/heads/stable') }}:
      - template: azure-pipelines/variables/${{ variables['Build.SourceBranchName'] }}.yml

parameters:
  - name: bicepDir
    displayName: Directory containing Folders with Bicep Templates
    type: string
    default: '$(bicepDir)'
  - name: bicepParameter
    displayName: Used in some templates to change Resource Name or other variables
    type: string
    values:
      - dev
      - stg
      - prod
    default: dev
  - name: resourceGroupName
    displayName: Name of the Resource Group where templates will be deployed to
    type: string
    default: '$(resourceGroupName)'
  - name: azureSubscription
    displayName: Name of the ARM-Service Connection
    type: string
    default: '$(azureSubscription)'
  - name: location
    displayName: Location where the Resources will be deployed
    type: string
    default: $(location)
  - name: agentpool
    displayName: Agent-Pool to be used
    values:
      - 'Azure Pipelines'
      - 'local'
    default: 'Azure Pipelines'

extends:
  template: azure-pipelines/stages/main.yml
  parameters:
    bicepDir: ${{ parameters.bicepDir }}
    bicepParameter: ${{ parameters.bicepParameter }}
    resourceGroupName: ${{ parameters.resourceGroupName }}
    azureSubscription: ${{ parameters.azureSubscription }}
    location: ${{ parameters.location }}
    agentpool: ${{ parameters.agentpool }}

main.yml

This stage-template contains is defining the complete pipeline. I had it separated from azure-pipelines.yml to be able to run validate_pr.yml (pull request validation) with the same stages than azure-pipelines.yml, but after development moved on I got rid of validate_pr.yml but thought it could still be practical later on to have this main.yml template.

azure-pipelines/main.yml
parameters:
  - name: bicepDir
    displayName: Directory containing Folders with Bicep Templates
    type: string
    default: IaC/bicep/deploy
  - name: bicepParameter
    displayName: Used in some templates to change Resource Name or other variables
    type: string
    values:
      - dev
      - stg
      - prod
    default: dev
  - name: resourceGroupName
    displayName: Name of the Resource Group where templates will be deployed to
    type: string
    default: $(resourceGroupName)
  - name: azureSubscription
    displayName: Name of the ARM-Service Connection
    type: string
    default: $(azureSubscription)
  - name: location
    displayName: Location where the Resources will be deployed to
    type: string
    default: 'westeurope'
  - name: agentpool
    displayName: Agent-Pool to be used
    values:
      - 'Azure Pipelines'
      - 'local'
    default: 'Azure Pipelines'

stages:
  - template: bicep_stage.yml
    parameters:
      bicepDir: ${{ parameters.bicepDir }}
      bicepParameter: ${{ parameters.bicepParameter }}
      resourceGroupName: ${{ parameters.resourceGroupName }}
      azureSubscription: ${{ parameters.azureSubscription }}
      location: ${{ parameters.location }}
      agentpool: ${{ parameters.agentpool }}

  # - ${{ if in(variables['Build.SourceBranchName'], 'main', 'stable') }}:
  #     - template: cleanup.yml
  #       parameters:
  #         agentpool: ${{ parameters.agentpool }}
  #         resourceGroupName: ${{ parameters.resourceGroupName }}

bicep_stage.yml

This stage is seperated in two jobs. where the first job is enumerating how often the second job needs to be run. Well, ok, this sounds a bit weird, but the secret is that it is not re-running the second job for x-times, but for every folder found in IaC/bicep/deploy. The steps which will then be done are found in the next section

azure-pipelines/bicep_stage.yml
parameters:
  - name: bicepDir
    displayName: Directory containing Folders with Bicep Templates
    type: string
  - name: bicepParameter
    displayName: Used in some templates to change Resource Name or other variables
    type: string
    values:
      - dev
      - stg
      - prod
  - name: resourceGroupName
    displayName: Name of the Resource Group where templates will be deployed to
    type: string
  - name: azureSubscription
    displayName: Name of the ARM-Service Connection
    type: string
  - name: location
    displayName: Location where the Resources will be deployed to
    type: string
  - name: agentpool
    displayName: Agent-Pool to be used
    values:
      - 'Azure Pipelines'
      - 'local'
    default: 'Azure Pipelines'

stages:
  - stage: bicep
    displayName: Bicep
    pool:
      name: ${{ parameters.agentpool }}
    jobs:
      - job: getTemplateFolders
        displayName: Get Template Folders
        steps:
          - task: PowerShell@2
            name: mtrx
            env:
              bicepDir: ${{ parameters.bicepDir }}
              bicepParameter: ${{ parameters.bicepParameter }}
              resourceGroupName: ${{ parameters.resourceGroupName }}
              azureSubscription: ${{ parameters.azureSubscription }}
              location: ${{ parameters.location }}
            inputs:
              targetType: filePath
              filePath: scripts/New-BicepMatrix.ps1
      - job: runner
        displayName: Test and Deploy
        dependsOn: getTemplateFolders
        condition: succeeded()
        strategy:
          matrix: $[ dependencies.getTemplateFolders.outputs['mtrx.legs'] ]
          maxParallel: 1
        steps:
          - template: jobs/steps/bicep_steps.yml
            parameters:
              bicepDir: $(bicepDir)
              bicepTemplateDir: $(bicepTemplateDir)
              bicepParameter: $(bicepParameter)
              resourceGroupName: $(resourceGroupName)
              azureSubscription: $(azureSubscription)
              location: $(location)
bicep_steps.yml

This step-template will:

  1. validate that the bicep is buildable (which is converting it to a ARM Template)
  2. create a resource group (no problem if called more than once and all runs belong to the same RG)
  3. do some verifications if the bicep is valid and deployable
  4. If conditions are met, it is calling another template to finally deploy the resources to Azure Resource Manager.
azure-pipelines/jobs/steps/bicep_steps.yml
parameters:
  - name: bicepDir
    type: string
    default: $(bicepDir)
  - name: bicepTemplateDir
    type: string
    default: $(bicepTemplateDir)
  - name: bicepParameter
    type: string
    default: $(bicepParameter)
  - name: resourceGroupName
    type: string
    default: $(resourceGroupName)
  - name: azureSubscription
    type: string
    default: $(azureSubscription)
  - name: location
    type: string
    default: $(location)

steps:
  - script: az bicep build --file main.bicep
    displayName: Run Bicep Linter
    name: LintBicepCode
    workingDirectory: $(bicepDir)/$(bicepTemplateDir)
  - template: create_resourceGroup.yml
    parameters:
      resourceGroupName: ${{ parameters.resourceGroupName }}
      azureSubscription: ${{ parameters.azureSubscription }}
      location: ${{ parameters.location }}
  - task: AzureCLI@2
    name: RunPreflightValidation
    condition: succeeded()
    displayName: Run preflight validation
    inputs:
      azureSubscription: ${{ parameters.azureSubscription }}
      workingDirectory: $(bicepDir)/$(bicepTemplateDir)
      scriptType: bash
      scriptLocation: inlineScript
      inlineScript: |
        function az_deploy() {
          az deployment ${deploymentScope} validate \
            ${deployTo} \
            --template-file main.bicep ${bicepParameterFile} ${location}
        }
        if [[ -e main.parameters.${{ parameters.bicepParameter }}.json ]]; then
          bicepParameterFile="--parameters=main.parameters.${{ parameters.bicepParameter }}.json"
        elif [[ -e main.parameters.json ]]; then
          bicepParameterFile="--parameters=main.parameters.json"
        else
          echo "will try to use template without parameterfile"
        fi
        if [[ -e mg ]]; then
          deploymentScope="mg"
          location="--location=${{parameters.location}}"
          mgs="$(cat mg)"
          for mg in ${mgs[@]}; do
            deployTo="--management-group-id=${mg}"
            az_deploy
          done
        else
          deploymentScope=group
          deployTo='--resource-group=${{ parameters.resourceGroupName }}'
          az_deploy
        fi
  - task: AzureCLI@2
    name: RunWhatIf
    displayName: Run what-if
    inputs:
      azureSubscription: ${{ parameters.azureSubscription }}
      workingDirectory: $(bicepDir)/$(bicepTemplateDir)
      scriptType: bash
      scriptLocation: inlineScript
      inlineScript: |
        function az_deploy() {
          az deployment ${deploymentScope} what-if \
            ${deployTo} \
            --template-file main.bicep ${bicepParameterFile} ${location}
        }
        if [[ -e main.parameters.${{ parameters.bicepParameter }}.json ]]; then
          bicepParameterFile="--parameters=main.parameters.${{ parameters.bicepParameter }}.json"
        elif [[ -e main.parameters.json ]]; then
          bicepParameterFile="--parameters=main.parameters.json"
        else
          echo "will try to use template without parameterfile"
        fi
        if [[ -e mg ]]; then
          deploymentScope="mg"
          location="--location=${{parameters.location}}"
          mgs="$(cat mg)"
          for mg in ${mgs[@]}; do
            deployTo="--management-group-id=${mg}"
            az_deploy
          done
        else
          deploymentScope=group
          deployTo='--resource-group=${{ parameters.resourceGroupName }}'
          az_deploy
        fi
  - task: AzureCLI@2
    name: DeployBicepFile
    condition: and(succeeded(), in('True', variables['isMain'], variables['isStable']))
    displayName: Deploy Bicep Template
    inputs:
      azureSubscription: ${{ parameters.azureSubscription }}
      scriptType: bash
      scriptLocation: inlineScript
      workingDirectory: $(bicepDir)/$(bicepTemplateDir)
      inlineScript: |
        function az_deploy() {
          az deployment ${deploymentScope} create \
            --name $(Build.BuildNumber) \
            ${deployTo} \
            --template-file main.bicep ${bicepParameterFile} ${location}
        }
        if [[ -e main.parameters.${{ parameters.bicepParameter }}.json ]]; then
          bicepParameterFile="--parameters=main.parameters.${{ parameters.bicepParameter }}.json"
        elif [[ -e main.parameters.json ]]; then
          bicepParameterFile="--parameters=main.parameters.json"
        else
          echo "will try to use template without parameterfile"
        fi
        if [[ -e mg ]]; then
          deploymentScope="mg"
          location="--location=${{parameters.location}}"
          mgs="$(cat mg)"
          for mg in ${mgs[@]}; do
            deployTo="--management-group-id=${mg}"
            az_deploy
          done
        else
          deploymentScope=group
          deployTo='--resource-group=${{ parameters.resourceGroupName }}'
          az_deploy
        fi
create_resourceGroup.yml

Well, I think the headline has already explained what this step is all about 🙊

azure-pipelines/jobs/steps/create_resourceGroup.yml
parameters:
  - name: resourceGroupName
    displayName: Resourcegroup Name
    type: string
  - name: azureSubscription
    displayName: Azure Service Connection
    type: string
  - name: location
    displayName: Resourcegroup Location
    type: string

steps:
  - task: AzureCLI@2
    condition: succeeded()
    displayName: Create RG ${{ parameters.resourceGroupName }}
    continueOnError: true
    inputs:
      azureSubscription: ${{ parameters.azureSubscription }}
      scriptType: bash
      scriptLocation: inlineScript
      inlineScript: |
        az group create \
          --name ${{ parameters.resourceGroupName }} \
          --location ${{ parameters.location }}

mkdocs-material

This pipeline is validating, building and deploying the documentation you are just looking at 😅

Validation is done with building the docs, if there is no error I suggest that everything will be fine since it is just a static website.

azure-pipelines/mkdocs-material.yml
trigger:
  paths:
    include:
      - docs
      - mkdocs.yml
      - azure-pipelines/mkdocs-material.yml
      - azure-pipelines/stages/jobs/steps/build_mkdocs.yml
      - scripts/deploy-mike.sh
      - src/requirements-mkdocs-material.txt
      - src/mkdocs-material

pr:
  - main

parameters:
  - name: pythonVersion
    displayName: Python Version to use when building MkDocs-Material
    type: string
    values:
      - '3.9'
      - '3.10'
    default: '3.9'
  - name: mkdocsSiteDir
    displayName: Name of the Directory where MkDocs will be built to
    type: string
    default: 'site'
  - name: agentpool
    displayName: Agent-Pool to be used
    values:
      - 'Azure Pipelines'
      - 'local'
    default: 'Azure Pipelines'

variables:
  - template: variables/default.yml

jobs:
  - job:
    displayName: MkDocs-Material
    pool:
      name: ${{ parameters.agentpool }}
    steps:
      - bash: 'export'
        displayName: 'debug export'
      - template: stages/jobs/steps/checkout_submodules.yml
        parameters:
          submodule: 'src/mkdocs-material'
          checkoutSelf: true
      - template: stages/jobs/steps/build_mkdocs.yml
        parameters:
          pythonVersion: '${{ parameters.pythonVersion }}'
          mkdocsSiteDir: '${{ parameters.mkdocsSiteDir }}'
      - task: Bash@3
        displayName: 'update gh-pages'
        inputs:
          targetType: filePath
          filePath: scripts/deploy-mike.sh

build_mkdocs.yml

azure-pipelines/stages/jobs/steps/build_mkdocs.yml
parameters:
  - name: pythonVersion
    displayName: Python Version to use
    values:
      - '3.9'
      - '3.10'
    type: string
    default: $(pythonVersion)
  - name: mkdocsSiteDir
    type: string
    default: 'site'

steps:
  - task: UsePythonVersion@0
    displayName: 'Use Python ${{ parameters.pythonVersion }}'
    inputs:
      versionSpec: '${{ parameters.pythonVersion }}'
      addToPath: true

  - script: pip install --upgrade pip wheel setuptools
    displayName: update tools

  - script: npm install --no-package-lock
    displayName: npm install
    workingDirectory: src/mkdocs-material

  - script: pip install -e src/mkdocs-material
    displayName: install MkDocs-Material

  - script: pip install -r requirements.txt
    displayName: install requirements

  - script: mkdocs build --site-dir ${{ parameters.mkdocsSiteDir }} --verbose
    displayName: build docs

devopsbuildagent.yml

This Pipeline will build a docker image of a DevOps-Agent, if you want to find out more about it's features, look here

azure-pipelines/devopsbuildagent.yml
trigger:
  branches:
    include:
      - refs/heads/main
      - update/*
      - feature/*
      - issue/*
  paths:
    include:
      - azure-pipelines/devopsbuildagent.yml
      - azure-pipelines/stages/jobs/build_devopsbuildagent_job.yml
      - src/DevOpsBuildAgent

schedules:
  - cron: '0 0 * * *'
    displayName: Nightly Build
    always: true
    branches:
      include:
        - main

pr: none

variables:
  - name: baseOS
    value: 'linux'
  - name: baseDistro
    value: 'ubuntu'
  - name: baseVersion
    value: '20.04'
  - name: dockerRegistry
    value: 'docker.io'
  - name: dockerhubuser
    value: 'mauwii'
  - name: dockerimage
    value: 'devopsbuildagent'

parameters:
  - name: matrix
    type: object
    displayName: Object which defines the matrix strategy
    default:
      amd64:
        baseArch: 'amd64'
        targetProc: 'x64'
      arm64v8:
        baseArch: 'arm64v8'
        targetProc: 'arm64'
  - name: maxParallelJobs
    type: number
    displayName: Defines how many Jobs can be running in parallel by the matrix strategy
    default: 2

stages:
  - stage: build
    jobs:
      - template: stages/jobs/build_devopsbuildagent_job.yml
        parameters:
          matrix: ${{ parameters.matrix }}
          maxParallelJobs: ${{ parameters.maxParallelJobs }}
  - stage: manifest
    dependsOn: build
    jobs:
      - template: stages/jobs/pushManifest_job.yml
        parameters:
          matrix: ${{ parameters.matrix }}
          maxParallelJobs: ${{ parameters.maxParallelJobs }}

cleanup_automation.yml

Necessary to use this Pipeline is a Service Principal with permission to delete Resources and Resource Locks

Besides a Service Connection in DevOps, add Secret Variables to the Pipeline for:

  • ApplicationId
  • ClientSecret
  • SpTenantId

This Pipeline will delete Resources in Subscriptions of the used Service Principal automatically. It differs between Resources which where available before and after a initial Date, which will have a defined range of days from creation before they will be deleted. While date is not reach, it will set a Tag on the Resource with the deletion date (which is just used for information). After the Resource has reached the defined age it will be deleted.

azure-pipelines/cleanup_automation.yml
trigger: none

pr: none

schedules:
  - cron: '0 0 * * *'
    always: true
    displayName: Daily cleanup
    branches:
      include:
        - main

steps:
  - task: AzurePowerShell@5
    displayName: 'Cleanup Script'
    inputs:
      azureSubscription: 'mngmtgrp001'
      ScriptType: 'FilePath'
      azurePowerShellVersion: 'LatestVersion'
      pwsh: true
      ScriptPath: 'scripts/cleanupautomation.ps1'
      ScriptArguments: -NewerResourceDays 7 -OlderResourceDays 14
      workingDirectory: 'scripts'
    env:
      ApplicationId: $(ApplicationId)
      ClientSecret: $(ClientSecret)
      SpTenantId: $(TenantId)
scripts/cleanupautomation.ps1

This is the Script which will be used to clean-up the Resources

param(
  [Switch]$WhatIf,
  [Int16]$NewerResourceDays,
  [Int16]$OlderResourceDays
)

# Days until Resources get deleted
if (-not($NewerResourceDays)) {
  $NewerResourceDays = 7
}
if (-not($OlderResourceDays)) {
  $OlderResourceDays = 14
}

# Set Initial Date
$InitialDate = (
  Get-Date `
    -Year 2022 `
    -Month 06 `
    -Day 15
).ToUniversalTime()

# Connect to Azure-CLI as Service Principal
[void](
  az login `
    --service-principal `
    -u $env:ApplicationId `
    -p $env:ClientSecret `
    -t $env:SpTenantId
)

# Get Current UTC-Time
$CurrentUTCtime = (
  Get-Date
).ToUniversalTime()

# Get Resources to be excluded
$cleanupexclusion = (
  Get-Content `
    -Path ./cleanupexclusion.json | ConvertFrom-Json
)

# Initialize Variables to count existing and deleted Resources/RGs
$AllAzRgCount = 0
$DeletedAzRgCount = 0
$AllAzResourceCount = 0
$DeletedAzResourceCount = 0

# Function to Print Information
function Write-Info {
  param (
    [System.String]$Title,
    [System.String]$Value,
    [Switch]$InitialNewLine,
    [Switch]$FinalNewLine
  )
  $Title = "${Title}: "
  $Value = "`t${Value}"
  if ($InitialNewLine) {
    $Title = "`n${Title}"
  }
  if ($FinalNewLine) {
    $Value = "${Value}`n"
  }
  Write-Host `
    -ForegroundColor Cyan `
    -NoNewline `
    $Title
  Write-Host $Value
}

# Get Azure Subscriptions
$AzSubscriptions = (
  Get-AzSubscription
)

# Iterate over Subscriptions
foreach ($AzSubscription in $AzSubscriptions) {

  # Set Context to current Subscription
  [void](
    Set-AzContext `
      -Subscription $AzSubscription.Id
  )
  [void](
    az account set `
      --subscription $AzSubscription.Id
  )

  # Get Resourcegroups of current Context
  $AzResourceGroups = (Get-AzResourceGroup)

  # Add Number of ResourceGroups to AllAzRgCount
  $AllAzRgCount += ($AzResourceGroups).Length

  # Get Number of Resources in current Context
  $AzResourceCount = (Get-AzResource).Length

  # Add Number of Resources in Subscription to AllAzResourceCount
  $AllAzResourceCount += $AzResourceCount

  # Write Info to Host about current Subscription
  Write-Info `
    -InitialNewLine `
    -Title "Subscription Name" `
    -Value $AzSubscription.Name
  Write-Info `
    -Title "Resourcegroups" `
    -Value $AzResourceGroups.Length
  Write-Info `
    -Title "Resource Count" `
    -Value "$AzResourceCount" `
    -FinalNewLine

  # Iterate over Resource Groups
  foreach ($AzResourceGroup in $AzResourceGroups) {

    # Get Resources in current Resource Group
    $AzResources = Get-AzResource `
      -ResourceGroupName $AzResourceGroup.ResourceGroupName

    # Write Info to Host about Current Resource Group
    Write-Info `
      -Title "Resource Group" `
      -Value $AzResourceGroup.ResourceGroupName
    Write-Info `
      -Title "Resource Count" `
      -Value $AzResources.Length

    # Iterate over Resources in current Resource Group
    $AzResources | ForEach-Object -Parallel {

      # Check if Resource should be excluded
      if ($using:cleanupexclusion.ResourceTypes -contains $_.Type `
          -or $using:cleanupexclusion.ResourceIDs -contains $_.ResourceId
      ) {
        Write-Host "Skipping" $_.ResourceName

        # Create/update Tag
        $Tag = @{
          "DeletionDate" = "skipped";
        }
        [void](
          Update-AzTag `
            -ResourceId $_.Id `
            -Tag $Tag `
            -Operation Merge `
            -ErrorAction:SilentlyContinue `
            -WhatIf:$using:WhatIf
        )
      }
      else {

        # Get Current Resource Creation Time
        $AzCurrentResource = (
          az resource list `
            --location $_.Location `
            --name $_.Name `
            --query "[].{Name:name, RG:resourceGroup, Created:createdTime, Changed:changedTime}" `
            -o json | ConvertFrom-Json
        )

        # Check if Resource was created before or after initial date to give devs more days to react on older resources
        if (($AzCurrentResource.Created).ToUniversalTime() -gt $using:InitialDate) {
          $DaysToDelete = (
            $using:NewerResourceDays - (
              $using:CurrentUTCtime - ($AzCurrentResource.Created).ToUniversalTime()
            ).Days
          )
        }
        else {
          $DaysToDelete = (
            $using:OlderResourceDays - ($using:CurrentUTCtime - $using:InitialDate).Days
          )
        }

        # Add/update Tag "DeletionDate" of Resource, or Delete it if defined age has been reached
        if ($DaysToDelete -gt 0) {

          # Write Info to Host when Resource will be deleted
          Write-Host (
            $AzCurrentResource.Name,
            "will get deleted on",
          (Get-Date).AddDays($DaysToDelete).ToString("yyyy-MM-dd")
          )
          # Create Tag
          $Tag = @{
            "DeletionDate" = "$((Get-Date).AddDays($DaysToDelete).ToUniversalTime().ToString("yyyy-MM-dd"))";
          }
          [void](
            Update-AzTag `
              -ResourceId $_.Id `
              -Tag $Tag `
              -Operation Merge `
              -ErrorAction:SilentlyContinue `
              -WhatIf:$using:WhatIf
          )
        }
        else {
          # Get Resource Lock
          $AzResourceLock = (
            Get-AzResourceLock `
              -ResourceName $_.Name `
              -ResourceType $_.Type `
              -ResourceGroupName $_.ResourceGroupName
          )

          # Remove Resource Lock if existing
          if ($AzResourceLock) {
            Write-Host "Deleting Resource Lock of $($_.Name)"
            [void](
              Remove-AzResourceLock `
                -LockId $AzResourceLock.LockId `
                -Force:$true `
                -WhatIf:$using:WhatIf
            )
          }

          # Remove Resource
          $RmResource = Remove-AzResource `
            -ResourceId $_.Id `
            -WhatIf:$using:WhatIf `
            -ErrorAction:SilentlyContinue `
            -Force:$true

          # Write Info and Increment Deleted Resource Count if succeeded
          if ($RmResource) {
            Write-Host "Deleted $($_.Name)"
          }
          else {
            Write-Host "Could not delete $($_.Name)"
          }
        }
      }
    }

    # Get Resourcecount in current Resource Group
    $AzRgResourceCountPostDeletion = (
      Get-AzResource `
        -ResourceGroupName $AzResourceGroup.ResourceGroupName
    ).Length

    # Delete Resourcegroup if empty
    if ($AzRgResourceCountPostDeletion -eq 0) {
      [void](
        Remove-AzResourceGroup `
          -Name $AzResourceGroup.ResourceGroupName `
          -Force:$true `
          -WhatIf:$WhatIf
      )

      # Write Info to Host that ResourceGroup was deleted
      Write-Info `
        -Title "Deleted Resourcegroup" `
        -Value $AzResourceGroup.ResourceGroupName `
        -FinalNewLine

      # Increment DeletedAzRgCount
      $DeletedAzRgCount++
    }
    else {
      # Write Info to Host that ResourceGroup is done
      Write-Info `
        -Title "Resourcegroup Done" `
        -Value $AzResourceGroup.ResourceGroupName `
        -FinalNewLine
    }
  }

  $AzResourceCountPostDeletion = (
    Get-AzResource
  ).Length
  $DeletedAzResourceCount += $AzResourceCount - $AzResourceCountPostDeletion

  # Write Info to Host that Subscription is done
  Write-Info `
    -Title "Subscription Done" `
    -Value $AzSubscription.Name `
    -FinalNewLine
}

# Write Info to Host about Resource-Counts
Write-Info `
  -Title "Deleted RGs" `
  -Value "`t$DeletedAzRgCount of $AllAzRgCount"
Write-Info `
  -Title "Deleted resources" `
  -Value "$DeletedAzResourceCount of $AllAzResourceCount"
scripts/cleanupexclusion.json

This is a configuration File for ResourceTypes/ResourceIDs which should be excluded.

1
2
3
4
5
6
7
{
  "ResourceTypes": [
    "Microsoft.KeyVault/vaults",
    "Microsoft.Storage/storageAccounts"
  ],
  "ResourceIDs": []
}

Last update: 2022-08-19