So I mentioned in my last post that I wanted to build out a repo that contained reusable components for a lot of my projects(That post can be found here). The goal was to consolidate a lot of the work I’m doing generally into things that could be reused by others.
Here is the release: https://github.com/mack-bytes-government/bicep-environment-templates/releases/tag/bicep-release-v1.0.0
And just for ease, here’s the repo: https://github.com/mack-bytes-government/bicep-environment-templates/
So to that end, I made a push today or bicep templates. I spend a lot of time in bicep, and honestly think it’s a really powerful tool for standing up and maintaining cloud infrastructure. The focus of that being that it provides all of the expected benefits of infrastructure as code.
For a full writeup on getting started with bicep, you can see my blog post here.
So to that end, I’ve published a bunch of templates to help those getting started, and they include the following:
- Basic Environment (a virtual network, configured with subnets and nsgs, that supports key vault, storage, and a container registry).
- Individual templates for:
- Container Registry
- Virtual Network
- Storage
- Container Registry
- Key Vault
- Data Science Virtual Machine
- Virtual Machine
Now, I will say as an interesting note, I also included a developer script which supports taking these templates and publishing them as bicep modules to a container registry. This is one of my favorite features of bicep, and it basically says that I can do the following:
If I have this template for “storage.bicep”:
param location string = resourceGroup().location
param storage_account_name string
param privateDnsZoneName string = 'privatelink.${environment().suffixes.storage}'
param subnet_id string
param vnet_id string
param default_tag_name string
param default_tag_value string
resource storage_account 'Microsoft.Storage/storageAccounts@2022-05-01' = {
name: storage_account_name
location: location
tags: {
'${default_tag_name}': default_tag_value
}
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
publicNetworkAccess: 'Disabled'
supportsHttpsTrafficOnly: true
allowBlobPublicAccess: false
networkAcls: {
defaultAction: 'Deny'
bypass: 'None'
ipRules: []
virtualNetworkRules: []
resourceAccessRules: []
}
}
}
resource private_dns_zone 'Microsoft.Network/privateDnsZones@2018-09-01' = {
name: privateDnsZoneName
location: 'global'
tags: {
'${default_tag_name}': default_tag_value
}
}
resource private_endpoint 'Microsoft.Network/privateEndpoints@2020-07-01' = {
name: '${storage_account_name}-pe'
location: location
tags: {
'${default_tag_name}': default_tag_value
}
properties: {
privateLinkServiceConnections: [
{
name: '${storage_account_name}-plsc'
properties: {
privateLinkServiceId: storage_account.id
groupIds: [
'blob'
]
}
}
]
manualPrivateLinkServiceConnections: []
subnet: {
id: subnet_id
}
}
}
resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2018-09-01' = {
name: '${private_dns_zone.name}-link'
parent: private_dns_zone
location: 'global'
tags: {
'${default_tag_name}': default_tag_value
}
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnet_id
}
}
}
resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2020-07-01' = {
name: 'default'
parent: private_endpoint
properties: {
privateDnsZoneConfigs: [
{
name: 'storageConfig1'
properties: {
privateDnsZoneId: private_dns_zone.id
}
}
]
}
}
output id string = storage_account.id
output private_endpoint_id string = private_endpoint.id
output private_dns_zone_id string = private_dns_zone.id
output name string = storage_account.name
And if I wanted to reference that module in another template in the same project, I could do the following:
module storage '../modules/storage.bicep' = {
name: 'storage'
params: {
storage_account_name: '${project_prefix}${env_prefix}stg'
location: location
subnet_id: existing_network.outputs.storage_subnet_id
vnet_id: existing_network.outputs.id
default_tag_name: default_tag_name
default_tag_value: default_tag_value
}
}
But what if I want to leverage this template as part of another project? That would be hard, I could try to do weird things with file paths, but that is going to be difficult and easily broken. I could copy and paste the file, but that would make it very hard to manage version changes.
But I could publish it to a container registry, using the OCI initiative standard.
I can run the following commands to publish this to an azure container registry:
az cloud set --name AzureUSGovernment
az login --use-device-code
az acr login --name {registry name}
az bicep publish --file {file path}/storage.bicep --target "br:{registry name}.azurecr.us/modules/storage:1.0.0"
What that buys me, is that I can then build separate bicep templates in other projects, I call this module in this manner:
module storage 'br:{registry name}.azurecr.us/modules/storage:1.0.0' = {
name: 'storage'
params: {
storage_account_name: '${project_prefix}${env_prefix}stg'
location: location
subnet_id: existing_network.outputs.storage_subnet_id
vnet_id: existing_network.outputs.id
default_tag_name: default_tag_name
default_tag_value: default_tag_value
}
}
The benefit to this is that there is no duplicated code, and when I make changes I can publish a newly tagged version “1.1.0” and anyone using “1.0.0” is not impacted.
To that end, I built a developer script to push all these templates into a container registry of your choice. The benefit then being that you can take them anywhere you need to for working with them.
My original hope was to publish these all as github packages, but unfortunately Github Packages does not support using “br:” tag (which stands for bicep registry). So this was not possible. But for right now, you can clone the repo, and then publish them to a private registry in your environment which is another use-case I was aiming for anyway.