It has been a while since I wrote about Azure Policy last time, plus recently there was a lot of hype around Bicep, so I decided to give it a try and shed some light on creating and deploying custom Azure policies with that new language.
I assume that you are already familiar with what Azure Policy is and how it works. If you are new to that really helpful and often underrated technology, I suggest checking out my Azure Policy Starter Guide.
Also, I recommend that you read through official Bicep documentation to get some notion about this new domain-specific language, which Microsoft promotes as an abstraction over ARM templates and Azure Resource Manager.
Bicep basics for Azure Policy
Like JSON-based ARM templates, Bicep is a declarative language that allows you to define desired Azure resource configuration and let Azure Resource Manager do its job of provisioning it. Initially, you had to compile a Bicep file into a regular ARM template to deploy your configuration. However, it is not the case anymore as both Azure CLI and Azure PowerShell now support deploying Bicep definitions. Note that input parameters for Bicep definitions are still come in the same format as for old-school ARM templates.
Apart from that, as we work with the same Azure service, you should expect that all Azure Policy specifics are applicable regardless of the language you use to define your configurations. So, all the tips and tricks you learned about creating, deploying and evaluating Azure Policy are still relevant.
Speaking of Bicep, you can define a single policy as well as a policy initiative, aka policy set, along with their assignment to a specific scope using the Bicep resource primitive. For example, to create a custom Azure Policy, you can define the following resource in your Bicep file:
As you might notice from that sample, in order for some policy-specific syntax to be valid, you should use backslash as an escape character for single quotation marks. Also, you shouldn’t use an additional forward square bracket in the expressions as it will be automatically added to the JSON during the Bicep build.
In the same manner, you can define your custom policy initiative:
As policy initiatives don’t define any rules, they use the ‘policyDefinitions’ keyword to reference existing policy definitions.
Policy and policy initiative assignments are also pretty straightforward and defined as yet another resource:
For complete definitions, look into bicep samples in my Azure Policy repository on GitHub. Besides, the Bicep product team and the community regularly update and create new sample Bicep definitions for various Azure services, including Azure Policy, so I suggest checking them for additional cases.
Now, when the basics are clear, let’s look into more advanced topics.
First of all, remember that you can deploy your custom Azure policy definitions at the subscription and management group levels only. At the same time, Azure Policy assignments can be created at the management group, subscription, and resource group levels. Also, keep in mind that the deployment scope of a policy, in turn, effectively limits its assignment scope.
The Bicep VS Code extension will warn you about the resources that cannot be deployed to the target scope, and the Bicep compiler will produce the compilation error.
In terms of Bicep definitions, you can scope your deployments by using the ‘targetScope’ keyword. Depending on that scope, a bicep file will be compiled into an ARM template using the corresponding deployment schema.
Using different deployment scopes will also impact the way you reference other resources in your configuration. If it is a new resource defined in the same Bicep file, then using standard syntax like ‘resourceSymbolicName.Id’ should be enough. However, when you need to reference an existing resource, e.g., a policy definition in policy initiative, first you should correctly define that resource in your Bicep file, and second, you should use the correct reference function:
- for Azure Policy definitions deployed at the subscription level use the ‘subscriptionResourceId’ function;
- for Azure Policy definitions deployed at the management group level use the ‘extensionResourceId’ function as custom policy definitions are implemented as resource extensions.
If you want to assign your policy at the tenant level, you should use the Tenant Root Group for that.
For example, to reference your existing policy definition deployed at the subscription level in a policy initiative, you can define it as the following:
Same referencing but at the management group level can be accomplished with the following syntax:
I don’t cover the Azure Policy exemptions feature here as it’s currently in preview and might change in the future.
Unfortunately, authoring Azure Policies with Bicep is still far from ideal. So, here are a few things that are not specifically related to Azure Policy, but rather Bicep language-generic features or their absence that annoyed me.
As I already mentioned, referencing policies from policy initiatives is a bit complicated and non-intuitive as you must be explicit about your reference scopes and always keep that nuance in mind. On a larger scale, when you have dozens of definitions that are defined in separate files and need to be deployed and assigned at different scopes, the new authoring experience is quite painful.
The next thing, which adds up to the negative authoring and debugging experience, is the lack of IntelliSense support for Azure Policy internal logic defined in Bicep files. What is more, the syntax highlighting for Bicep-defined Azure Policy rules in VS Code is also very limited. Apart from that, I was also unpleasantly surprised that, in contrast to the ARM template authoring experience, Bicep will not warn you about unused parameters or variables you have in your files.
Lastly, the way you currently define human-readable names and descriptions for policy parameters using the Bicep parameter decorators looks a bit awkward for me:
Fortunately, the Bicep product team makes good progress in implementing new Bicep features and providing the community with simpler and better options for defining Azure infrastructure as code, and I’m impatiently looking forward to new Bicep releases.
If you were writing lots of ARM templates and haven’t tried Bicep yet, I certainly recommend you check it out and post your impressions in the comments below!