Unlocking the Power of Azure Verified Modules for Infrastructure as Code and Platform Engineering - Part 2
Stephen Tulp
December 10, 2024
9 minutes to read
In yesterdays blog we covered some fundamentals of Azure Verified Modules (AVM) and why they are a great North Star to strive for on your Azure Platform Engineering journey. Today we will look at some practical examples that showcase the benefits.
During the year I posted on some new capabilities released and how I went about creating IaC templates for them.
These were created using local modules and provided templates for the teams to be able to use. Both of these resources are now available as AVM modules (October for Fabric and December for Elastic SAN). I am going to show how what was already a concise IaC template is even easier now with the AVM version.
Current State
The current IaC code is available in the Advent Calendar - AVM repo under the before folders for each resource. There are 3 files we need to deploy this resource
- .bicep module template that covers the required components for the resource provider.
- .bicepparam parameter template that covers the values and settings for the deployment
- .ps1 PowerShell script that runs the Resource Group deployment task to deploy into Azure.
Future State
The future IaC code is available in the Advent Calendar - AVM repo under the after folders for each resource. There are 2 files we need to deploy this resource
- .bicepparam parameter template that covers the values and settings for the deployment
- .ps1 PowerShell script that runs the Resource Group deployment task to deploy into Azure.
As we are leveraging the AVM module, this negates the need for the .bicep module locally and instead we are pulling it from the Public Module Registry (MCR)
Differences
Lets go through the changes to see what has changed and how the IaC is simplified with the introduction of AVM.
Bicepparam Parameter File
Not a lot of changes here, the main thing of not is the using attribute, we are specifying the path to the AVM module over the local bicep module that I created. The other minor change is the adminUsers array parameter, this is what i called it over the AVM adminMembers
The Elastic SAN template differences are also the same, but I also created a User Defined Type for the elasticSan parameter that groups values in the object.
Bicep Module File
The main changes and simplification comes from not needing to have the module and template locally. The bicep file I created is below and has Metadata, parameters, resources and outputs that I have defined.
metadata name = 'Microsoft Fabric Module'
metadata description = 'Bicep Module used to deploy Microsoft Fabric Capacity'
metadata author = 'Insight APAC Platform Engineering'
// Parameters
@description('Required. The name of the Fabric Capacity, needs to be globally unique and lowercase.')
param name string
@description('Optional. The Azure Region to deploy the resources into.')
param location string = resourceGroup().location
@description('Optional. The SKU name of the Fabric Capacity.')
@allowed([
'F2'
'F4'
'F8'
'F16'
'F32'
'F64'
'F128'
'F256'
'F512'
'F1024'
'F2048'
])
param skuName string = 'F2'
@description('Optional. The SKU tier of the Fabric Capacity instance.')
param skuTier string = 'Fabric'
@description('Required. The list of administrators for the Fabric Capacity instance.')
param adminUsers array
@description('Optional. Tags that will be applied to all resources in this module.')
param tags object = {}
// Resource: Microsoft Fabric Capacity
resource fabricCapacity 'Microsoft.Fabric/capacities@2022-07-01-preview' = {
name: toLower(name)
location: location
tags: tags
sku: {
name: skuName
tier: skuTier
}
properties: {
administration: {
members: adminUsers
}
}
}
// Outputs
@description('The ID of the Fabric Capacity.')
output resourceId string = fabricCapacity.id
@description('The name of the Fabric Capacity.')
output resourceName string = fabricCapacity.name
The Fabric AVM module is quite basic compared to others but you can see the completeness of the module and includes telemetry, locks and other common attributes that are part of the AVM specification. We don’t actually care about the module, need to know the complexities of the module because we are consuming it from the ACR and parsing in the parameters. When you look at something like the Azure Storage Module is complex as it caters for all different storage account deployments blob, file, NFS, redundancy types etc.
metadata name = 'Fabric Capacities'
metadata description = 'This module deploys Fabric capacities, which provide the compute resources for all the experiences in Fabric.'
metadata owner = 'Azure/module-maintainers'
@description('Required. Name of the resource to create.')
param name string
@description('Optional. Location for all resources.')
param location string = resourceGroup().location
@description('Optional. Tags of the resource.')
param tags object?
@allowed([
'F2'
'F4'
'F8'
'F16'
'F32'
'F64'
'F128'
'F256'
'F512'
'F1024'
'F2048'
])
@description('Optional. SKU tier of the Fabric resource.')
param skuName string = 'F2'
@allowed(['Fabric'])
@description('Optional. SKU name of the Fabric resource.')
param skuTier string = 'Fabric'
@description('Required. List of admin members. Format: ["something@domain.com"].')
param adminMembers array
import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.2.1'
@description('Optional. The lock settings of the service.')
param lock lockType?
@description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true
// ============== //
// Resources //
// ============== //
#disable-next-line no-deployments-resources
resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) {
name: '46d3xbcp.res.fabric-capacity.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
properties: {
mode: 'Incremental'
template: {
'$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
contentVersion: '1.0.0.0'
resources: []
outputs: {
telemetry: {
type: 'String'
value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
}
}
}
}
}
resource fabricCapacity 'Microsoft.Fabric/capacities@2023-11-01' = {
name: name
location: location
tags: tags
sku: {
name: skuName
tier: skuTier
}
properties: {
administration: {
members: adminMembers
}
}
}
resource fabricCapacity_lock 'Microsoft.Authorization/locks@2016-09-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
name: lock.?name ?? 'lock-${name}'
properties: {
level: lock.?kind ?? ''
notes: lock.?kind == 'CanNotDelete'
? 'Cannot delete resource or child resources.'
: 'Cannot delete or modify the resource or child resources.'
}
scope: fabricCapacity
}
// ============ //
// Outputs //
// ============ //
@description('The name of the resource group the module was deployed to.')
output resourceGroupName string = resourceGroup().name
@description('The resource ID of the deployed Fabric resource.')
output resourceId string = fabricCapacity.id
@description('The name of the deployed Fabric resource.')
output name string = fabricCapacity.name
@description('The location the resource was deployed into.')
output location string = fabricCapacity.location
PowerShell Deployment Script
To deploy the solution we have a basic PowerShell script that is doing a Resource Group deployment. One of the cool features and something I wasn’t aware of until recently is that the -TemplateFile parameter is now optional. In the past, you needed to have both the -TemplateFile and -TemplateParameterFile parameters but now its not needed as you are consuming from the MCR and using the AVM module.
Conclusion
Over the last two days we have delved into Azure Verified Modules and gained a better understanding of how we can use them and the benefits they bring to Platform Engineering. Later this week we will take it a step further and use a pattern module for Subscription Vending. If AVM peaks your interest then check out the quarterly AVM Community Calls to see past calls and dates for future ones.