Azure Spring Clean 2025: Auditing Azure

A community-driven initiative focused on improving Azure governance, security, and cost management through best practices and automation.

What is Azure Spring Clean

The Azure Spring Clean community initiative is an annual event focused on improving and optimizing Azure environments by promoting best practices, governance, security, and cost management. It encourages cloud professionals, developers, and IT admins to review their Azure resources, clean up unused services, optimize costs, and improve security and compliance.

We should also give a massive shout out to Joe Carlyle and Thomas Thornton for hosting Azure Spring Clean!

Azure Quick Review (AZQR)

Azure Quick Review (AZQR) is an open-source tool developed by Microsoft to quickly assess the security, compliance, and best practices of Azure resources. It provides automated checks and generates reports to help organizations identify misconfigurations, security risks, and optimization opportunities.

For more information you can check the docs page here and the Git repository here

Key Features

  • Automated Assessments: Scans various Azure services, including compute, networking, security, storage, and databases.
  • Best Practices Validation: Compares configurations against Microsoft’s best practices.
  • Security and Compliance: Identifies security vulnerabilities, missing policies, and compliance gaps.
  • Report Generation: Outputs findings in a structured report (Excel format) for easy review and action.

Use Cases

  • Cloud Governance: Ensures compliance with security and operational guidelines.
  • Security Audits: Identifies potential vulnerabilities in Azure deployments.
  • Cost Optimization: Provides recommendations to improve resource efficiency.

Infrastructure As Code

Deployment Time! I’ve spent some time building out a Function App and some Infrastructure As Code to help streamline this process. You can check the project over on GitHub bwc-azure-quick-review-functionapp.

Required Modules and Packages

Azure CLI
Azure Bicep
Az Module (PowerShell)
Microsoft Graph Module (PowerShell)*

* Required for Graph Permissions for the Managed Identity

During the deployment, we deploy the following resources:

Deployment Notes

90% of the bicep uses the Azure Verified Modules, However there are some custom modules for RBAC Assignment. This this to allow the Managed Identity Reader access to the subscription.

Deployment Time

Once you’ve cloned the repository, From the Infra folder execute the following powershell

1
.\Invoke-AzDeployment.ps1 -targetscope sub -subscriptionId '' -customerName '' -environmentType [ dev | acc | prod] -location '' -deploy

Once deployed, You can copy the contents of the FunctionAppCode into the function app.

Deployment ZIP Notice

Currently, ZIP package deployment has some issues. I’ll update this post once a stable approach is available.

Function App Configuration

From Functions > App Files, We need to update the profile.ps1 and requirements.psd1

profile.ps1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Azure Functions profile.ps1
#
# This script runs during a "cold start" of your Function App.
# A "cold start" happens when:
#
# * The Function App starts for the first time.
# * The Function App starts after being de-allocated due to inactivity.
#
# You can use this file to define helper functions, run commands, or set environment variables.
# NOTE: Any non-environment variables will be reset after the first execution.

# Authenticate with Azure PowerShell using Managed Identity (MSI) if Managed Identity ID is available.
if ($env:managedIdentityId) {

    # Disable automatic Az context saving for the current process to avoid conflicts.
    Disable-AzContextAutosave -Scope Process | Out-Null

    # Authenticate to Azure using Managed Identity.
    Write-Host "Authenticating with Azure using Managed Identity..."
    Connect-AzAccount -Identity -AccountId $env:managedIdentityId | Out-Null

    # Retrieve the Azure access token for Microsoft Graph API.
    $azAccessToken = (Get-AzAccessToken -AsSecureString -ResourceUrl "https://graph.microsoft.com").Token

    # Connect to Microsoft Graph using the access token.
    Write-Host "Authenticating with Microsoft Graph..."
    Connect-MgGraph -AccessToken $azAccessToken | Out-Null

    Write-Host "Authentication successful."
}
else {
    Write-Warning "Managed Identity ID is not found. Azure authentication will be skipped."
}

requirements.psd1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
    # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'.
    # To use the Az module in your function app, please uncomment the line below.
    'Az.Accounts'                                  = '4.*'
    'Microsoft.Graph.Authentication'               = '2.*'
    'Microsoft.Graph.Identity.DirectoryManagement' = '2.*'
}

From Overview > Functions > Create Function > Timer Function

AzureQuickReview
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<#
.SYNOPSIS
    This script performs an Azure Quick Review scan, generates a report, and optionally sends the report via email.

.DESCRIPTION
    The script performs the following steps:
    1. Ensures the report directory exists.
    2. Downloads the latest Azure Quick Review tool.
    3. Executes the Azure Quick Review scan and generates a report.
    4. Optionally sends the generated report via email if email sending is enabled.

.PARAMETER Timer
    A parameter that can be used to trigger the script execution based on a timer.

.PARAMETER folderName
    The name of the directory where the Azure Quick Review tool and reports will be stored.

.PARAMETER azQuickReviewFilePath
    The file path of the generated Azure Quick Review report.

.FUNCTIONS
    Test-Directory
        Ensures the specified directory exists. If it does not exist, it creates the directory.

    Invoke-DownloadAzureQuickReview
        Downloads the latest release of the Azure Quick Review tool from GitHub.

    Invoke-AzureQuickReviewScan
        Executes the Azure Quick Review scan and generates a report in the specified directory.

    Send-ReportByEmail
        Sends the generated Azure Quick Review report via email if email sending is enabled.

.NOTES
    - Ensure that the necessary environment variables are set for email sending.
    - The script uses managed identity for authentication with Azure.

#>

param($Timer)

# Define Report Directory
$folderName = 'azqrReports'

# Ensure directory exists
function Test-Directory {
    param($folderPath)

    if (-Not (Test-Path -Path $folderPath -PathType Container)) {
        try {
            New-Item -ItemType Directory -Path $folderPath -Force | Out-Null
            Write-Output "Directory '$folderPath' created successfully."
        } catch {
            Write-Error "Failed to create directory '$folderPath': $_"
            exit 1
        }
    }
}

# Download Azure Quick Review
function Invoke-DownloadAzureQuickReview {
    param($folderPath)

    Write-Output "Downloading Azure Quick Review..."
    try {
        $azQRLatestReleaseTagUrl = 'https://api.github.com/repos/Azure/azqr/releases/latest'
        $azQRLatestReleaseTag = (Invoke-RestMethod -Uri $azQRLatestReleaseTagUrl).tag_name
        $azQRDownloadUrl = "https://github.com/Azure/azqr/releases/download/$azQRLatestReleaseTag/azqr-ubuntu-latest-amd64"

        Invoke-WebRequest -Uri $azQRDownloadUrl -OutFile "./$folderPath/azqr"
        chmod +x "./$folderPath/azqr"
        Write-Output "Azure Quick Review downloaded successfully."
    } catch {
        Write-Error "Failed to download Azure Quick Review: $_"
        exit 1
    }
}

# Execute Azure Quick Review Scan
function Invoke-AzureQuickReviewScan {
    param($folderPath)

    Write-Output "Executing Azure Quick Review Scan..."
    try {
        $env:AZURE_ORG_NAME = (Get-MgOrganization).DisplayName
        $env:AZURE_TENANT_ID = (Get-AzContext).Tenant.Id
        $env:AZURE_CLIENT_ID = $env:managedIdentityId

        # Report Naming
        $dateTime = Get-Date -Format 'yyyy_MM_dd_HH_mm_ss'
        $reportName = "$($dateTime)_$($env:AZURE_ORG_NAME)_azure_review"

        Write-Output "Report: [$reportName]"

        # Execute Azure Quick Review
        & "./$folderPath/azqr" scan --output-name "./$folderPath/$reportName"

        $script:azQuickReviewFilePath = (Get-ChildItem -Path ./$folderPath/*.xlsx | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
        Write-Output "Azure Quick Review scan completed successfully. Report saved at $azQuickReviewFilePath."
    } catch {
        Write-Error "Azure Quick Review execution failed: $_"
        exit 1
    }
}

# Send Email with Report (if enabled)
function Send-ReportByEmail {
    param($azQuickReviewFilePath)

    if ($env:emailEnabled) {
        Write-Output "Sending the report via email..."
        try {
            # Fetch environment variables
            $smtpServer = $env:emailSMTPServer
            $smtpPort = $env:emailSMTPServerPort
            $smtpUser = $env:emailSMTPAuthUserName
            $smtpPassword = $env:emailSMTPAuthPassword

            # Email details
            $from = $env:emailSender
            $to = $env:emailRecipient
            $date = Get-Date -Format 'MMMM yyyy'
            $subject = "[Azure Quick Review] - New Advisory Report - $date"
            $body = @"
Hello,

This is your Azure Quick Review report.
Tenant Name: $env:AZURE_ORG_NAME
Tenant ID: $env:AZURE_TENANT_ID

Please find the detailed findings in the attached report.

Best regards,
Azure Quick Review Automation
"@

            # Create email message
            $emailMessage = New-Object System.Net.Mail.MailMessage($from, $to, $subject, $body)
            $emailMessage.IsBodyHtml = $false
            $emailMessage.Priority = [System.Net.Mail.MailPriority]::High

            # Add attachment if available
            if ($azQuickReviewFilePath) {
                $attachment = New-Object System.Net.Mail.Attachment -ArgumentList $azQuickReviewFilePath
                $emailMessage.Attachments.Add($attachment)
            }

            # Configure SMTP client
            $smtpClient = New-Object System.Net.Mail.SmtpClient($smtpServer, $smtpPort)
            $smtpClient.Credentials = New-Object System.Net.NetworkCredential($smtpUser, $smtpPassword)
            $smtpClient.EnableSsl = $true

            # Send email
            $smtpClient.Send($emailMessage)
            Write-Output "Email sent successfully."

            # Cleanup
            $emailMessage.Dispose()
            if ($attachment) { $attachment.Dispose() }
        } catch {
            Write-Error "Failed to send email: $_"
            exit 1
        }
    }
}

# Main logic
Test-Directory -folderPath $folderName
Invoke-DownloadAzureQuickReview -folderPath $folderName
Invoke-AzureQuickReviewScan -folderPath $folderName
Send-ReportByEmail -azQuickReviewFilePath $azQuickReviewFilePath

How it works

The Function App is configured as a timer-triggered function, automating the Azure Quick Review process. It:

  • Downloads the latest release of Azure Quick Review.

  • Conducts an audit of Azure resources.

  • Generates a report in Excel format.

  • Sends the report via email using SMTP authentication.

SMTP authentication settings can be configured in the Bicep parameter file.

In the biceep param file, You can configure the SMTP Auth settings.

NOTE

For testing, I used Sendgrid and it worked without issue 👏

params.bicepparam snippet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Email - SMTP Configuration
@description('Enable Email Notifications')
param emailEnabled = false

@description('Configure SMTP Server Address')
param emailSMTPServer = ''

@description('Configure SMTP Server Port')
param emailSMTPPort = 587

@description('Configure SMTP Server Username')
param emailSMTPAuthUserName = ''

@description('Configure SMTP Server Password')
param emailSMTPAuthPassword = ''

@description('Configure Email Sender')
param emailSender = ''

@description('Configure Email Recipient')
param emailRecipient = ''

Example Email Notification

Azure Quick Review Report Summary

The Excel report generated by Azure Quick Review (AZQR) provides an in-depth overview of your Azure environment, highlighting security risks, compliance gaps, and optimization opportunities. The report is structured into multiple tabs, each offering specific insights into different aspects of your Azure resources.

Report Sections & Breakdown

  • Recommendations

    • Provides a summary of key security, compliance, and configuration recommendations.
    • Highlights areas where best practices are not being followed.
    • Includes suggested actions to improve security posture, optimize costs, and enhance operational efficiency.
  • Impacted Resources

    • Lists the Azure resources affected by the identified issues.
    • Categorizes findings based on severity (Critical, High, Medium, Low).
    • Helps prioritize remediation efforts.
  • Resource Types

    • Displays the distribution of different resource types found in the environment.
    • Useful for understanding the Azure services in use.
  • Inventory

    • A comprehensive list of all discovered Azure resources.
    • Provides metadata such as resource name, type, region, subscription, and tags.
    • Useful for asset management and governance.
  • Advisor

    • Extracts relevant recommendations from Azure Advisor.
    • Covers areas such as reliability, security, performance, cost optimization, and operational excellence.
    • Helps align with Microsoft’s Well-Architected Framework.
  • Defender Recommendations

    • Provides insights from Microsoft Defender for Cloud.
    • Lists security vulnerabilities and recommendations for remediation.
    • Helps ensure compliance with security best practices.
  • Out of Scope

    • Lists any resources that were excluded from the analysis.
    • Can be useful to understand if certain resources need to be manually reviewed.
  • Defender

    • Detailed breakdown of Defender for Cloud findings, including security alerts and recommendations.
    • Helps organizations strengthen their security posture.
  • Costs

    • Highlights potential cost savings based on Azure Cost Management insights.
    • Identifies underutilized resources and provides right-sizing recommendations.
    • Useful for budgeting and cost optimization.
  • Pivot Table

    • A dynamic table allowing for custom data analysis.
    • Enables filtering and sorting of report findings for deeper insights.

Using the Report

  • Prioritize Security Fixes
    Focus on the Recommendations, Impacted Resources, and Defender Recommendations tabs first.

  • Optimize Costs
    Review the Costs and Advisor sections for efficiency improvements.

  • Improve Governance
    Use the Inventory and Resource Types tabs for tracking Azure resource usage.

By leveraging the insights from this report, organizations can enhance security, improve compliance, and optimize costs within their Azure environment.

Wrap Up

Automating Azure Quick Review as part of your governance strategy can save time, enhance security, and improve compliance. By integrating this tool into a scheduled monthly review, you ensure that your Azure environment remains aligned with best practices, security standards, and the Well-Architected Framework (WAF).

Regular reviews help identify misconfigurations, security risks, and cost inefficiencies before they become critical issues. Automating these checks reduces manual effort and provides a consistent, repeatable process for maintaining a secure and optimized cloud environment.

By leveraging Infrastructure as Code (IaC) and automation, you can seamlessly incorporate these reviews into your workflows, enabling proactive governance and continuous improvement.

If you found this valuable, consider sharing your feedback or contributing to the Azure Quick Review GitHub project. Stay secure, stay optimized, and happy auditing! 🚀

Share with your network!

Built with Hugo - Theme Stack designed by Jimmy