Overview
In the ever-expanding landscape of Azure services, maintaining a coherent and standardized naming convention for resources is a familiar challenge for cloud practitioners. We’ve all found ourselves navigating through documentation, juggling abbreviations to ensure consistency. To simplify this common struggle, I have written a Python script. It automates the extraction of Azure resource abbreviations, ensuring a more personalized and efficient approach to managing cloud nomenclature.
Python Script
import argparse
import json
import re
import requests
from bs4 import BeautifulSoup
def fetch_resource_abbreviations(url) -> dict[str, str]:
response = requests.get(url)
soup = BeautifulSoup(markup=response.text, features="html.parser")
content_div = soup.find(name="div", attrs={"class": "content"})
tables = content_div.find_all("table")
data = {}
acronyms = ("AI|IP|VM|BI|HD|DB|SQL|CDN|IoT|API|SignalR|WebPubSub|StorSimple|ExpressRoute")
regex = r"^([A-Z][a-z]*$)|.*(?:{}).*".format(acronyms)
for table in tables:
rows = table.find_all("tr")
for row in rows[1:]:
cells = row.find_all("td")
resource = cells[0].get_text()
abbreviation = cells[2].get_text()
# Ignore entries where the abbreviation is enclosed in < and >
if not (abbreviation.startswith("<") and abbreviation.endswith(">")):
# Split the resource string into words
words = re.split(r"\W+", resource)
# Convert each word to title case unless it's already in camel case
words = [word if re.match(regex, word) else word.title() for word in words]
# Join the words back together and remove any remaining non-alphanumeric characters
resource = "".join(words)
data[resource] = abbreviation
return data
def main(file_path) -> None:
url = "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations"
data = fetch_resource_abbreviations(url)
with open(file=file_path, mode="w") as f:
json.dump(obj=data, fp=f, indent=2)
f.write("\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-f",
"--file",
help="The path to the file where the data will be written",
default="abbreviations.json",
)
args = parser.parse_args()
main(args.file)
This Python script is designed to scrape a webpage for Azure resource abbreviations and save them to a JSON file.
It uses the requests
library to fetch the webpage and beautifulsoup4
to parse the HTML. The fetch_resource_abbreviations function
retrieves the webpage, finds the relevant data in the HTML tables, and returns a dictionary mapping resource names to their abbreviations.
The main function calls fetch_resource_abbreviations
and then writes the resulting data to a file. The script uses argparse
to allow the user to specify the output file path via command line arguments. If no file path is provided, the data is written to abbreviations.json
by default.
To run the script, you can use the following command:
python update_resource_abbreviations.py -f ./bicep/abbreviations.json
Example Output
abbreviations.json
{
"AISearch": "srch",
"AzureAIServicesMultiServiceAccount": "aisa",
"AzureAIVideoIndexer": "avi",
"AzureMachineLearningWorkspace": "mlw",
"AzureOpenAIService": "oai",
"BotService": "bot",
"ComputerVision": "cv",
"ContentModerator": "cm",
"ContentSafety": "cs",
"CustomVisionPrediction": "cstv",
"CustomVisionTraining": "cstvt",
"DocumentIntelligence": "di",
"FaceAPI": "face",
"HealthInsights": "hi",
"ImmersiveReader": "ir",
"LanguageService": "lang",
"SpeechService": "spch",
"Translator": "trsl",
"AzureAnalysisServicesServer": "as",
"AzureDatabricksWorkspace": "dbw",
"AzureDataExplorerCluster": "dec",
"AzureDataExplorerClusterDatabase": "dedb",
"AzureDataFactory": "adf",
"AzureDigitalTwinInstance": "dt",
"AzureStreamAnalytics": "asa",
"AzureSynapseAnalyticsPrivateLinkHub": "synplh",
"AzureSynapseAnalyticsSQLDedicatedPool": "syndp",
"AzureSynapseAnalyticsSparkPool": "synsp",
"AzureSynapseAnalyticsWorkspaces": "synw",
"DataLakeStoreAccount": "dls",
"DataLakeAnalyticsAccount": "dla",
"EventHubsNamespace": "evhns",
"EventHub": "evh",
"EventGridDomain": "evgd",
"EventGridSubscriptions": "evgs",
"EventGridTopic": "evgt",
"EventGridSystemTopic": "egst",
"HDInsightHadoopCluster": "hadoop",
"HDInsightHbaseCluster": "hbase",
"HDInsightKafkaCluster": "kafka",
"HDInsightSparkCluster": "spark",
"HDInsightStormCluster": "storm",
"HDInsightMlServicesCluster": "mls",
"IoTHub": "iot",
"ProvisioningServices": "provs",
"ProvisioningServicesCertificate": "pcert",
"PowerBIEmbedded": "pbi",
"TimeSeriesInsightsEnvironment": "tsi",
"AppServiceEnvironment": "ase",
"AppServicePlan": "asp",
"AzureLoadTestingInstance": "lt",
"AvailabilitySet": "avail",
"AzureArcEnabledServer": "arcs",
"AzureArcEnabledKubernetesCluster": "arck",
"BatchAccounts": "ba",
"CloudService": "cld",
"CommunicationServices": "acs",
"DiskEncryptionSet": "des",
"FunctionApp": "func",
"Gallery": "gal",
"HostingEnvironment": "host",
"ImageTemplate": "it",
"ManagedDiskOs": "osdisk",
"ManagedDiskData": "disk",
"NotificationHubs": "ntf",
"NotificationHubsNamespace": "ntfns",
"ProximityPlacementGroup": "ppg",
"RestorePointCollection": "rpc",
"Snapshot": "snap",
"StaticWebApp": "stapp",
"VirtualMachine": "vm",
"VirtualMachineScaleSet": "vmss",
"VirtualMachineMaintenanceConfiguration": "mc",
"VMStorageAccount": "stvm",
"WebApp": "app",
"AksCluster": "aks",
"AksSystemNodePool": "npsystem",
"AksUserNodePool": "np",
"ContainerApps": "ca",
"ContainerAppsEnvironment": "cae",
"ContainerRegistry": "cr",
"ContainerInstance": "ci",
"ServiceFabricCluster": "sf",
"ServiceFabricManagedCluster": "sfmc",
"AzureCosmosDBDatabase": "cosmos",
"AzureCosmosDBForApacheCassandraAccount": "coscas",
"AzureCosmosDBForMongoDBAccount": "cosmon",
"AzureCosmosDBForNoSQLAccount": "cosno",
"AzureCosmosDBForTableAccount": "costab",
"AzureCosmosDBForApacheGremlinAccount": "cosgrm",
"AzureCosmosDBPostgreSQLCluster": "cospos",
"AzureCacheForRedisInstance": "redis",
"AzureSQLDatabaseServer": "sql",
"AzureSQLDatabase": "sqldb",
"AzureSQLElasticJobAgent": "sqlja",
"AzureSQLElasticPool": "sqlep",
"MariaDBServer": "maria",
"MariaDBDatabase": "mariadb",
"MySQLDatabase": "mysql",
"PostgreSQLDatabase": "psql",
"SQLServerStretchDatabase": "sqlstrdb",
"SQLManagedInstance": "sqlmi",
"AppConfigurationStore": "appcs",
"MapsAccount": "map",
"SignalR": "sigr",
"WebPubSub": "wps",
"AzureManagedGrafana": "amg",
"APIManagementServiceInstance": "apim",
"IntegrationAccount": "ia",
"LogicApp": "logic",
"ServiceBusNamespace": "sbns",
"ServiceBusQueue": "sbq",
"ServiceBusTopic": "sbt",
"ServiceBusTopicSubscription": "sbts",
"AutomationAccount": "aa",
"ApplicationInsights": "appi",
"AzureMonitorActionGroup": "ag",
"AzureMonitorDataCollectionRules": "dcr",
"Blueprint": "bp",
"BlueprintAssignment": "bpa",
"DataCollectionEndpoint": "dce",
"LogAnalyticsWorkspace": "log",
"LogAnalyticsQueryPacks": "pack",
"ManagementGroup": "mg",
"MicrosoftPurviewInstance": "pview",
"ResourceGroup": "rg",
"TemplateSpecsName": "ts",
"AzureMigrateProject": "migr",
"DatabaseMigrationServiceInstance": "dms",
"RecoveryServicesVault": "rsv",
"ApplicationGateway": "agw",
"ApplicationSecurityGroupAsg": "asg",
"CDNProfile": "cdnp",
"CDNEndpoint": "cdne",
"Connections": "con",
"DnsForwardingRuleset": "dnsfrs",
"DnsPrivateResolver": "dnspr",
"DnsPrivateResolverInboundEndpoint": "in",
"DnsPrivateResolverOutboundEndpoint": "out",
"Firewall": "afw",
"FirewallPolicy": "afwp",
"ExpressRouteCircuit": "erc",
"ExpressRouteGateway": "ergw",
"FrontDoorStandardPremiumProfile": "afd",
"FrontDoorStandardPremiumEndpoint": "fde",
"FrontDoorFirewallPolicy": "fdfp",
"FrontDoorClassic": "afd",
"IPGroup": "ipg",
"LoadBalancerInternal": "lbi",
"LoadBalancerExternal": "lbe",
"LoadBalancerRule": "rule",
"LocalNetworkGateway": "lgw",
"NatGateway": "ng",
"NetworkInterfaceNic": "nic",
"NetworkSecurityGroupNsg": "nsg",
"NetworkSecurityGroupNsgSecurityRules": "nsgsr",
"NetworkWatcher": "nw",
"PrivateLink": "pl",
"PrivateEndpoint": "pep",
"PublicIPAddress": "pip",
"PublicIPAddressPrefix": "ippre",
"RouteFilter": "rf",
"RouteServer": "rtserv",
"RouteTable": "rt",
"ServiceEndpointPolicy": "se",
"TrafficManagerProfile": "traf",
"UserDefinedRouteUdr": "udr",
"VirtualNetwork": "vnet",
"VirtualNetworkGateway": "vgw",
"VirtualNetworkManager": "vnm",
"VirtualNetworkPeering": "peer",
"VirtualNetworkSubnet": "snet",
"VirtualWan": "vwan",
"VirtualWanHub": "vhub",
"AzureBastion": "bas",
"KeyVault": "kv",
"KeyVaultManagedHsm": "kvmhsm",
"ManagedIdentity": "id",
"SshKey": "sshkey",
"VpnGateway": "vpng",
"VpnConnection": "vcn",
"VpnSite": "vst",
"WebApplicationFirewallWafPolicy": "waf",
"WebApplicationFirewallWafPolicyRuleGroup": "wafrg",
"AzureStorSimple": "ssimp",
"BackupVaultName": "bvault",
"BackupVaultPolicy": "bkpol",
"FileShare": "share",
"StorageAccount": "st",
"StorageSyncServiceName": "sss",
"AzureLabServicesLabPlan": "lp",
"VirtualDesktopHostPool": "vdpool",
"VirtualDesktopApplicationGroup": "vdag",
"VirtualDesktopWorkspace": "vdws",
"VirtualDesktopScalingPlan": "vdscaling"
}
Usage with Bicep
To use the produced JSON file in a Bicep file, you can load the file’s contents into a variable and use it to generate resource names.
@sys.description('Abbreviations for resource names.')
# disable-next-line no-unused-vars
var abbreviations = sys.loadJsonContent('./abbreviations.json')
// usage: "${abbreviations.<resource_name>}"
If you use an editor like Visual Studio Code with the Bicep extension, you can use the abbreviations
variable to generate resource names with IntelliSense.
Usage with Terraform
To use the produced JSON file in a Terraform file, you can load the file’s contents into a local variable and use it to generate resource names.
locals {
abbreviations = jsondecode(file("${path.module}/abbreviations.json"))
}
# usage: "${local.abbreviations.<resource_name>}"
GitHub Actions
You can automate the process of updating the abbreviations JSON file by using GitHub Actions. Here’s an example workflow that runs the Python script on a schedule and creates a pull request if the file has changed.
# File: update-resource-abbreviations.yaml
name: Update Resource Abbreviations
on:
schedule:
- cron: '0 6 ** 1' # Runs at 06:00, only on Monday
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
update-abbreviations:
runs-on: ubuntu-latest
permissions:
issues: write
contents: write
pull-requests: write
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests beautifulsoup4
- name: Run update script
run: python ./.github/scripts/update_resource_abbreviations.py -f ./bicep/abbreviations.json
- name: Check for changes
id: git-diff
run: |
change=false
if git status --porcelain | grep -E 'bicep/abbreviations.json'; then
change=true
fi
echo "changed=$change" >> $GITHUB_OUTPUT
- name: Create/Update branch and commit changes
if: steps.git-diff.outputs.changed == 'true'
env:
BRANCH_NAME: update-resource-abbreviations
run: |
git pull origin main
git switch ${{ env.BRANCH_NAME }} || git switch -c ${{ env.BRANCH_NAME }}
git config --global user.name 'GitHub Actions'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add bicep/abbreviations.json
git commit -m 'Update resource abbreviations'
git push --set-upstream origin ${{ env.BRANCH_NAME }}
# Make sure to enable *Allow GitHub Actions to create and approve pull requests* under Settings > Actions
- name: Create pull request
if: steps.git-diff.outputs.changed == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH_NAME: update-resource-abbreviations
run: |
gh pr create \
--base ${{ github.ref_name }} \
--head ${{ env.BRANCH_NAME }} \
--title "Update resource abbreviations" \
--body "Automated changes by GitHub Actions regarding the *bicep/abbreviations.json* file."
Summary
Automating the extraction and maintenance of Azure resource abbreviations with this Python script enhances efficiency and consistency in resource naming practices. By keeping abbreviations up-to-date, developers and cloud practitioners can ensure clarity and conformity in resource naming conventions, contributing to a well-organized and easily understandable Azure infrastructure.
Leave a comment