How to deploy CML 2.9 to Azure using Terraform
Cisco Modeling Labs (CML) is a network simulation tool that I’m using as part of my CCNP studies. It’s affordable—I bought a personal license during Cisco’s Black Friday sale for $125 CAD, but there is also a free license that lets you run 5 or fewer nodes!—and can be deployed on cloud infrastructure to create a rich and extensible learning environment.
I opted to deploy CML in the cloud because I didn’t have an extra computer powerful enough to run it, and I didn’t want to buy and maintain more hardware. For my needs, infrastructure-as-a-service (IaaS) is relatively cheap, flexible, and convenient. Plus, it provides an opportunity to expand my understanding of cloud deployment and dip my toes into network automation.
CML cloud deployment frustrations
When I discovered CML’s cloud deployment tool chain, I felt a bit intimidated because the documentation assumes a basic familiarity with IaaS administration, which I did not have.
I also felt intimidated by the tool chain’s use of Terraform, a software with which I was unfamiliar. I wanted to spend my time and energy using CML, not learning another software just to get it up and running.
But, after many hours of frustrating attempts to deploy CML via alternate means, I relented, and took my time going through the tool chain documentation.
Eventually, I figured it out.
My humble contribution
Although Cisco’s cloud deployment documentation is helpful, it’s more geared toward Amazon Web Services (AWS). If you want to deploy to Azure, the documentation isn’t as detailed, and you need to do some additional legwork and cross-referencing to get things running. I can appreciate why someone new to IaaS in Azure may wish to have someone walk them through the steps.
What follows is my attempt at making explicit the step-by-step instructions for deploying CML 2.9 in Azure using Terraform. My hope is that this will help beginners who might have otherwise given up.
1. Create and configure Azure services
Before you deploy CML, you need to ensure your Azure account and services are properly configured.
1.1. Create Azure account and subscription
- Sign up for an Azure account at https://signup.azure.com/signup.
- As of July 2025, Microsoft offers a free (30-day) $200 USD credit toward its Azure products and services.
- From the Azure web portal, search for Subscriptions in the top search bar.
- Select the Subscriptions service from the search results.
- Click the Add button to start creating a new subscription.
- Complete the Create a subscription wizard to finish setup.
1.2. Install Azure CLI
Azure Command-Line Interface (CLI) is a cross-platform command-line tool to administer Azure resources. My guide uses Azure CLI to create and configure Azure resources, but you can also use the Azure Portal if you prefer a graphical user interface (GUI).
See Microsoft’s How to install the Azure CLI article for information about installing the tool.
On macOS, Azure CLI is installed using the Homebrew package manager.
- Open Terminal and run
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"to install Homebrew. - Run
brew update && brew install azure-clito update Homebrew’s repository information and install Azure CLI.
1.3. Authorise Azure CLI
- Run
az loginto connect Azure CLI to Azure. - Authenticate to Azure in the web browser window that opens.
- After you’ve authenticated, return to Azure CLI, and select the subscription you created in step 1.1.
- Copy your subscription ID for later use.
1.4. Create resource group
- Run
az group create --name <resource-group> --location <location>to create a resource group, a container that holds related resources in Azure.- To find a list of supported regions for your subscription, run
az account list-locations --query "[].{Region:name}" --output table.
- To find a list of supported regions for your subscription, run
1.5. Create storage resources
-
Run
az storage account create --name <storage-account> --resource-group <resource-group> --location <location> --sku Standard_LRS --encryption-services blobto create a storage account. -
Run
az ad signed-in-user show --query id -o tsv | az role assignment create --role "Storage Blob Data Contributor" --assignee @- --scope "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>"to assign the currently authenticated user the Storage Blob Data Contributor role, which is required in order to create and operate on a blob storage container.- If you forgot to copy your subscription ID from earlier, run
az account showand copy theidvalue.
- If you forgot to copy your subscription ID from earlier, run
-
Run
az storage container create --account-name <storage-account> --name <container> --auth-mode loginto create a blob storage container.- “Blob” storage is designed for unstructured data, such as binary data; e.g. CML reference platform images.
1.6. Create SSH key pair
-
Run
az sshkey create --name <ssh-key-name> --resource-group <resource-group> --encryption-type Ed25519to create an SSH key pair. Note the file path to the SSH key pair. -
Run
ls <ssh-file-path>to confirm thatid_ed25519andid_ed25519.pubdo not already exist.- If they don’t already exist, proceed with the following steps; otherwise, note the private key file name for use in step 6.1, and move to step 1.6.4.
-
Run
mv <ssh-file-path>/<generated-private-key> <ssh-file-path>/id_ed25519 && <ssh-file-path>/<generated-public-key> <ssh-file-path>/id_ed25519.pubto rename your SSH key pair to CML’s SSH server’s expected value. -
Run
chmod 600 <ssh-file-path>/<private-key>to update the private key permissions.
2. Upload CML images and software
Although you can use Azure CLI to upload data to a blob container, Microsoft also publishes an alternative tool specifically designed for high-performance data transfer: AzCopy.
- In my experience, uploading numerous large files to Azure blob storage via AzCopy can be a hit-or-miss experience. If for some reason AzCopy isn’t working reliably, try uploading files via the Azure web portal GUI.
2.1. Download AzCopy
See Microsoft’s Get started with AzCopy article for more information about installing the AzCopy.
On an Apple Silicon macOS device, use the AzCopy Arm64 preview portable binary.
- Download the AzCopy Arm64 preview portable binary.
- Double-click the file to unzip it.
- In Terminal, run
cd <file-path-to-azcopy>to change your working directory to where the AzCopy binary file was extracted.
2.2. Authorise AzCopy
- Run
az account show --query tenantId -o tsvto find your Azure tenant ID, which will be used in the next step. - Run
./azcopy login --tenant-id <tenant-id>to connect AzCopy to your storage account. - Copy the authorisation code displayed in the command output.
- Open a web browser, navigate to https://microsoft.com/devicelogin, paste the code, and click Next.
- Authenticate to Azure, and click Continue to confirm that you are trying to sign in to Azure Storage AzCopy.
2.3. Download CML images and software
- Log in to Cisco Learning Network Store’s My Account page to find your active CML order.
- Click View license(s) in the appropriate CML order, and click copy to copy the registration token. Keep this value for later use.
- Click Download to be redirected to the CML-Personal 2.9.0 Software Download page.
- Download cml2_p_2.9.0-3_amd64-3-pkg.zip and refplat_p-20250616-fcs-iso.zip.
- Double-click the files to unzip them. Note the file paths for later use.
- Run the signature verification programs per the
READMEfiles’ instructions to verify the integrity of the downloaded files. - Double-click refplat-20250616-fcs.iso to mount the volume.
- Run
./azcopy copy "<pkg-path>/cml2_2.9.0-3_amd64-3.pkg" "https://<storage-account>.blob.core.windows.net/<container>/cml2_2.9.0-3_amd64-3.pkg"to copy the CML server upgrade package to your blob storage container. - Run
./azcopy copy "<refplat-volume-path>/REFPLAT/*" "https://<storage-account>.blob.core.windows.net/<container>/refplat" --recursiveto copy the node-definitions and virl-base-images folders to your blob storage container.
3. Configure cloud-cml tool chain
Now that Azure services have been created, configured, and your blob storage container has the required CML images and software, you’re ready to configure Cisco DevNet’s cloud-cml tool chain.
3.1. Clone cloud-cml repository
- Run
git clone https://github.com/CiscoDevNet/cloud-cml.git <repo-file-path>to clone the remote GitHub repository to your local device.
3.2. Prepare the tool chain for Azure
- Run
<repo-file-path>/prepare.shto modify and prepare the tool chain. - Enter no when asked to enable AWS.
- Enter yes when asked to enable Azure.
- Enter no when asked to enable CyberArk Conjur.
- Enter no when asked to enable Hashicorp Vault.
3.3. Install and configure Terraform
See Hashicorp’s Install Terraform web page for more information about installing Terraform.
On macOS, you can install Terraform using Homebrew.
- Run
brew tap hashicorp/tapto install Hashicorp’s official repository of Homebrew packages. - Run
brew install hashicorp/tap/terraformto install Terraform. - Update
<repo-file-path>/variables.tfto include your Azure subscription and tenant IDs, replacingnotsetwhere appropriate.- If you forgot to copy these IDs from earlier, run
az account show.
- If you forgot to copy these IDs from earlier, run
3.4. Edit config.yml configuration file
- Update
<repo-file-path>/config.ymlto ensure the following values are updated.
target: azure
[...]
azure:
resource_group: <resource-group>
size: <compute_size>
[...]
storage_account: <storage-account>
container_name: <container>
[...]
common:
key_name: <ssh-key-name>
[...]
secret:
secrets:
smartlicense_token:
raw_secret: <registration_token>
[...]
app:
software: cml2_2.9.0-3_amd64-3.pkg
[...]
license:
flavor: CML_Personal
- Update
<repo-file-path>/config.ymlto comment out any refplat images you don’t want to load during deployment.- This speeds up deployment and saves you a bit of money, due to fewer storage blob container access charges.
4. Deploy CML
It’s now time to test your work—but don’t hold your breath! (Seriously, deployment can take 5-10 minutes. Go get a drink of water.)
- Run
cd <repo-file-path>to change your working directory to your local cloud-cml repository. - Run
terraform initto initialise your Terraform configuration. - Run
terraform applyto apply your configuration. - Review the proposed deployment plan, and enter
yesto begin deployment. - Wait 5–10 minutes for Terraform to deploy CML to Azure. (Get that drink of water!)
- If deployment is successful, you will be presented the following output.
cml2info = {
"address" = "<ip-address>"
"del" = "ssh -p1122 sysadmin@<ip-address> /provision/del.sh"
"url" = "https://<ip-address>"
"version" = "2.9.0+build.3"
}
5. Use CML
Congratulations! You did it! Now comes the fun part—the reason why you were messing about with Terraform in the first place: labbing!
5.1. Access CML web portal
- Navigate to the CML web portal using the URL per step 4.6.
- If you need the URL again, run
terraform output cml2info.
- If you need the URL again, run
- Run
terraform output cml2secretsto get the generated secrets. - Log in with username
adminand the appropriate secret displayed in the previous step.
5.2. CML resources
New to CML? Want a great introduction? Check out Cisco U’s free Introduction to Network Simulations with Cisco Modeling Labs (CMLLAB) course.
You’ll also find lots of great, detailed information in Cisco’s CML User’s Guide.
Finally, there’s a ton of great content in Cisco DevNet’s cml-community GitHub repository!
6. Destroy CML
Fun time is over, but you’re not done yet! Make sure you destroy all the Azure resources Terraform created for you, otherwise you’re looking at a bill that could cost you hundreds (or thousands!) of dollars.
- You’ll probably want to keep the CML images and software in your blob storage container so they’re ready for the next deployment, but I don’t know your budget, so check out the Azure Pricing Calculator to get a rough idea of costs.
6.1. Remove CML license
Before destroying CML, it’s important that you remove its license, otherwise the license is not available for use by subsequent deployments.
- Ensure all labs are stopped, and no VMs are running.
- Run the
delcommand presented to you after successful deployment. - (Optional) If the previous command doesn’t work, you can manually remove the CML license by selecting, from within the CML web interface, Tools → Licensing → Actions → Deregister.
6.2. Terraform destroy
- Run
terraform destroyfrom within your local cloud-cml repository to destroy all the Azure resources Terraform created for you. - Wait a minute or two for Terraform to destroy CML.
- Check the Azure Portal to ensure that the resources have, indeed, been destroyed; if not, manually remove them from within the GUI. (Or try your hand at Azure CLI!)