Our team has been working on developing automation for deploying a secure and scalable cloud foundation in Microsoft Azure. As we delved into Microsoft’s Cloud Adoption Framework and began experimenting with the Enterprise-scale Landing Zone conceptual architecture, we discovered a relatively recent DevOps offering from Microsoft called AzOps. After spending some time with this framework, I’d like to pass along our learnings and insights!
What is AzOps?
If you’re reading this article, we’ll assume you’re already familiar with, or at least aware of, the concept of Infrastructure-as-code and its many wonderful advantages. So, we can safely skip the obligatory intro on that topic and move on to list the current IaC options for working with Azure.
Your biggest players of course are Terraform and the Azure-native ARM and Bicep templates. Additionally, you have options like Pulumi and Farmer, which gives you a code-based abstraction layer; allowing you to represent large, complex infrastructures very efficiently.
So where does AzOps fit into this? Well, it’s not so much a framework or DSL for declaring infrastructure state, but rather Microsoft’s implementation of the Infrastructure-as-code philosophy. Simply put, AzOps is an integrated CI/CD pipeline solution that provides the ability to discover, deploy, and manage Azure infrastructure via ARM or Bicep templates at all Azure scope levels. It is comprised of two components: the AzOps-Accelerator template repository, which offers the CI/CD pipelines in either GitHub Actions or Azure Pipelines flavors, and a PowerShell library that supports the pipelines.
A closer look
Let’s get more familiar with the AzOps project. We’ll begin with the AzOps core PowerShell module, then examine the CI/CD pipelines offered in the AzOps-Accelerator repository.
AzOps Module
As we mentioned above, AzOps is Microsoft’s implementation of the IaC philosophy, but the declarative IaC framework already existed in the form of ARM templates. The next logical step to further enable the adoption of the IaC approach is to offer a set of CI/CD pipelines that harness ARM templates to manage infrastructure. Supporting that in an appropriately modular fashion meant it was necessary to build a library of PowerShell functions. It is this module that forms the core of AzOps:
Looking at the File List in the PowerShell Gallery, you can see that there is a set of internal functions and classes such as Get-AzOpsSubscription.ps1, New-AzOpsDeployment.ps1, and Get-AzOpsRole.ps1. These are built upon PowerShell’s native Azure cmdlets and are used to both discover existing and create new Azure infrastructure.
This set of internal functions is then used to create the three main, high-level functions: Initialize-AzOpsEnvironment.ps1, Invoke-AzOpsPull.ps1, and Invoke-AzOpsPush.ps1. These functions in turn align with the core CI/CD pipelines in the AzOps-Accelerator template repository.
AzOps Accelerator
The AzOps Accelerator is a template repository that implements the Azure integrated CI/CD pipelines, offering them via two CI/CD platforms: GitHub Actions and Azure Pipelines. Getting up and running is as simple as generating your own repo from the template and configuring Azure authentication.
We won’t delve into the configuration details here, for that you can refer to the AzOps Accelerator Wiki. Now let’s take a closer look at the pipeline functionality of AzOps Accelerator…
Note that GitHub Actions uses the term workflows, but for consistency, I’ll continue using the term pipeline. Out of the box, the AzOps Accelerator provides three pipelines that support managing infrastructure: Push, Pull, and Validate. These pipelines can be extended, and new ones added if more advanced CI/CD capabilities are required. A fourth pipeline, called Update, is available for keeping your repo’s pipeline code up to date with any changes/additions from the AzOps Accelerator template repo. Of course, I have not put these through the paces of managing a production infrastructure, but my testing finds all four of them work very well and as advertised!
Here are some additional details on each:
Push Pipeline
The Push pipeline is what really enables the Infrastructure-as-code approach. You manage infrastructure by creating, updating, or deleting the ARM templates in the repo and then running the Push pipeline to deploy those changes to Azure. The pipeline will run automatically on any commits to the main branch, either directly or via Pull Request. Upon creation of a PR (and on subsequent PR commits), the Validate pipeline will run (see below). When the PR is merged, the Push pipeline will run and deploy the changes.
Validate Pipeline
The Validate pipeline is designed to be run on Pull Requests. On creation of a Pull Request, or commits to the PR, the Validate pipeline will look for changes, validate them against the Azure Resource Manager, and add a PR comment with "WhatIf" results showing the proposed changes. Below is an example showing the “WhatIf” result for the creation of a new Resource Group:
Pull Pipeline
The Pull pipeline is an extremely useful and unique addition to the CI/CD capabilities of an IaC Repo and enables teams to utilize a hybrid approach to managing infrastructure. Infrastructure can be managed via an IaC-first approach, using the Push pipeline, but you can also safely create, update, and delete infrastructure in the Portal. The Pull pipeline manages configuration drift by querying the Azure Resource Manager, determining any changes with respect to the repo’s main branch, and then opening and merging a Pull Request to integrate those changes. The Pull pipeline is configured to run every 6 hours via cron and can also be run manually on-demand.
Update Pipeline
The Update pipeline is provided by the AzOps team as a means of keeping your repo’s pipeline code up to date with the latest changes and/or additions to the AzOps-Accelerator template Repo. It is intended to be run manually, and on-demand, and will detect differences in the pipeline code (in .github and .pipelines) between your repo (origin) and the AzOps-Accelerator template Repo (upstream). If changes are found, it will create a PR to merge the changes/updates from upstream.
One important note: the Update pipeline will detect ANY differences in the pipeline code, so if you have made any changes, customizations, or extensions, those would be overwritten by the PR, returning you to the original state. As such, the Update pipeline should only be used if you do NOT intend to make any substantive changes to the original template.
Should you consider adopting AzOps?
Now that we understand what AzOps is and how it works, let’s get down to some of the pros and cons of adopting AzOps and look at whether this is a good fit for your organization.
Pros
- Rapid, easy setup using the AzOps-Accelerator template
- Provides a great jump start into using Infrastructure-as-code with a solid, out-of-the-box CI/CD platform
- Fairly easy to understand and use
- Project is under active development with a responsive development team
- Generates a straightforward, hierarchical repository structure that aligns to the Management Group, Subscription, Resource Group, and Resource hierarchy you see in the Azure Portal
- Easily extensible CI/CD platform via GitHub Actions or Azure Pipelines
- Contains an Update pipeline to keep your Repo up to date with the latest changes/additions to the AzOps-Accelerator template Repo
Finally, I want to call out the following two features separately, because they differentiate AzOps from other IaC and CI/CD frameworks. Enabled by the Pull pipeline described above:
- You can start using AzOps at any time: the ability to discover and easily import all Azure resources as ARM templates allows you to start using AzOps at any time with existing infrastructure. This can be done with other frameworks like Terraform or Pulumi, but it would be much more challenging
- You can simultaneously manage infrastructure from both the repo (IaC-first) and the Azure Portal: other frameworks require an IaC-only methodology. Provisioning resources in the Azure Portal would result in configuration drift and/or an inconsistent state; but with AzOps, you are free to do either and it works seamlessly
Cons
- Only supports deploying to Azure: For the foreseeable future, ARM and Bicep templates only support deploying Azure resources. If you require multi-cloud or cross-platform support, you will need to look at Terraform or Pulumi
- AzOps is a relatively new project with associated limited adoption
- Currently only supports ARM templates for the Pull pipeline: This means you can deploy with Bicep templates but discovered infrastructure will be imported as ARM templates. Bicep template generation for Pull pipeline is on the roadmap, but it might be some time before this feature is available
- Only supports representing and managing resources with discrete, explicit ARM templates: You cannot, within the context of using the provided AzOps pipelines, take advantage of parameterizing ARM templates to represent and deploy multiple resources with a single template
Is it really Infrastructure-as-Code?
I really like the spirit of the AzOps implementation and greatly appreciate the team’s work on this project and their responsiveness to my questions. Unfortunately, I would not consider the AzOps implementation to be Infrastructure-as-code.
Bold statement, I know, but hear me out.
It is certainly true that you have a full representation of your infrastructure in source control, you can manage that infrastructure from the repo via a CI/CD pipeline, and you can control those changes via Pull Requests—all very good things. However, AzOps seems to fall short in that it fails to embrace the true nature of code.
The foundational nature of code is that it is dynamic and flexible. Both ARM and Bicep templates provide this dynamic ability by using parameters, wherein you can define a single, parameterized template that can be used to deploy multiple resources. Currently, the AzOps module and CI/CD pipelines do not support this, for example, by providing an ability to define a variables file to be used as input to a set of parameterized templates. Rather, you must define a discrete and explicit ARM template for every single resource. So, you end up with less Infrastructure-as-code and more of a “config database". Without the means to take advantage of template parameterization, managing your infrastructure will become cumbersome, often requiring you to create and/or edit multiple files where one should suffice.
Why is this case? Well, the two most exciting features of AzOps are the ability to safely provision infrastructure either from the repo or the Portal and the ability to easily start using AzOps against existing infrastructure. Again, these two fantastic features are an AzOps value add that you will not find in any other IaC framework. However, supporting these features via the Pull pipeline seems to effectively preclude the use of template parameterization. It would certainly be possible for AzOps to introduce support for using parameterized templates in the Push pipeline. But it would be extremely difficult at best to support the reverse operation in the Pull pipeline, specifically, generating the parameterized templates and associated deployment code based on the Azure Resource Manager query results. That leaves the 1:1, ARM-template:resource format as the only way to realistically implement the Pull pipeline.
Is AzOps a fit for your organization?
Upon initial use of the AzOps project and the Accelerator, I was fairly optimistic and thought it might become a regular part of our cloud recommendations. It provides intriguing features; has an elegant, simple implementation; and most importantly––it works. Unfortunately, a couple of the glaring deficiencies I highlighted above just outweigh some of the standout features. With that in mind, my feeling is that the scope of potential adopters would be limited to smaller organizations that:
- Are fully committed to Azure as their sole cloud provider: If there will ever be a need to support other public cloud providers or private cloud platforms, consider other options
- Don’t mind working with ARM templates: You can deploy with Bicep templates, but it may be a while before the Pull pipeline supports Bicep generation
- Have limited Infrastructure-as-code experience and would like to gain exposure to the concept and practice of IaC
- Place a high priority on the option to manage their infrastructure both from an IaC approach and the Azure Portal
- Are primarily looking for a way to maintain a complete ARM template export of their infrastructure
- Desire a simple, easy-to-implement IaC solution to manage a small infrastructure
- Value the explicit, hierarchical representation of their infrastructure and don’t mind the code repetition
But even for these organizations, I suggest carefully considering longer-term Infrastructure-as-code requirements. You can adopt AzOps with your existing infrastructure at any time, but it’s far more difficult to begin using other IaC frameworks, like Terraform, with existing infrastructure. If you start with AzOps, you may find yourself stuck as that initially small infrastructure grows, and managing it with hundreds of ARM templates becomes tedious. So think carefully about how your infrastructure might scale and whether it’s worth investing the time into learning something like Terraform or Pulumi, which may provide more longevity. You’ll have to write your own CI/CD pipeline, but it will likely be worth it in the long run and there are plenty of resources to help you with this.
Tell us about your experience with AzOps. Or message me to learn more and to see if it’s the right fit for your organization.