Azure SQL Managed Instance: Network Intent Policy error fix in Azure Bicep

Photo of author

Dan Rios

3 min read

Introduction

"Network Security Group doesn't have supporting Security Rule for Network Intent Policy Security Rule"

If you’re trying to deploy Azure SQL Managed Instance in Azure via Azure Bicep you may have stumbled across this ‘Network Intent Policy’ error when trying to redeploy your Azure Bicep template.

The problem doesn’t present itself on your first deployment yet when trying your template again this error is present. It seems if we don’t define the network security group rules and routes for the SQL Managed Instance network intent explicitly in our Bicep template then ARM believes something is either changing or being redeployed. To me, this goes against the idempotent nature of ARM/Bicep to a degree.

At the time, I was referencing the CARML modules for Azure SQL Managed Instance for what I should include in my template, which did have some rules defined around network intent policy rules. However, these were not working for me. I later discovered the rules defined were the pre November 2022 mandatory rules and so not valid for my deployment.

In my searches I could not find any off the shelf Bicep code for the current rules, so I decided to put something together in hope it helps others get around the error quicker than I did.

What is the network intent policy?

In order for Azure SQL Managed Instance to function correctly, it needs certain rules and routes defined and those rules must remain in place. Microsoft state:

“To improve service security, manageability, and availability, SQL Managed Instance applies a network intent policy on some elements of the Azure virtual network infrastructure. The policy configures the subnet, the associated network security group, and the route table to ensure that the minimum requirements for SQL Managed Instance are met. “


Connectivity architecture – Azure SQL Managed Instance | Microsoft Learn

High level connectivity architecture for Azure SQL Managed Instance from Microsoft Learn.

The fix to make your Azure Bicep templates idempotent

In order to fix the network intent policy error and have a Bicep template that can be redeployed over and over again we must define the network intent policy rules in the Bicep template after initial deployment.

Here is the current NSG security rules & route table routes for Azure SQL Managed Instance with Bicep that I’ve put together. Built from the following Azure SQL Managed Instance connectivity table here.

https://gist.github.com/riosengineer/3cbb4bf725030d0ee54f3944a35427d8

// SQL MI NSG and Route table with mandatory subnet service-aided config: https://learn.microsoft.com/en-gb/azure/azure-sql/managed-instance/connectivity-architecture-overview?view=azuresql&tabs=current#mandatory-security-rules-with-service-aided-subnet-configuration
//
// where modules defined are copied locally from CARML/Azure Verified Modules GitHub Repositories. https://github.com/Azure/ResourceModules/tree/main/modules/network/network-security-group & https://github.com/Azure/ResourceModules/tree/main/modules/network/route-table
//
//
// Azure Bicep with complete list of current Azure SQL MI mandatory security rules & routes for repeatable template deployments without template violations
// Insert this to your Azure SQL MI Bicep templates to avoid Network Intent Policy errors when redeploying Bicep templates for Azure SQL MI and avoid erorrs like: https://github.com/Azure/azure-quickstart-templates/issues/6670
@description('Enter Azure SQL MI subnet address prefix.')
param subnetPrefix string = '10.1.2.0/24'
var subnetPrefixString = replace(replace(subnetPrefix, '.', '-'), '/', '-')
module nsg 'modules/network-security-group/main.bicep' = {
name: '${uniqueString(deployment().name, location)}-nsg'
params: {
name: nsgName
location: location
securityRules: [
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${subnetPrefixString}-v11'
properties: {
description: 'Allow Azure Load Balancer inbound traffic'
protocol: '*'
sourcePortRange: '*'
sourceAddressPrefix: 'AzureLoadBalancer'
destinationAddressPrefix: subnetPrefix
destinationPortRange: '*'
access: 'Allow'
priority: 100
direction: 'Inbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${subnetPrefixString}-v11'
properties: {
description: 'Allow MI internal inbound traffic'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: subnetPrefix
access: 'Allow'
priority: 101
direction: 'Inbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-aad-out-${subnetPrefixString}-v11'
properties: {
description: 'Allow communication with Azure Active Directory over https'
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: 'AzureActiveDirectory'
access: 'Allow'
priority: 102
direction: 'Outbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-onedsc-out-${subnetPrefixString}-v11'
properties: {
description: 'Allow communication with the One DS Collector over https'
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: 'OneDsCollector'
access: 'Allow'
priority: 103
direction: 'Outbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${subnetPrefixString}-v11'
properties: {
description: 'Allow MI internal outbound traffic'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: subnetPrefix
access: 'Allow'
priority: 104
direction: 'Outbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-strg-p-out-${subnetPrefixString}-v11'
properties: {
description: 'Allow outbound communication with storage over HTTPS'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: 'Storage.uksouth'
access: 'Allow'
priority: 105
direction: 'Outbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-strg-s-out-${subnetPrefixString}-v11'
properties: {
description: 'Allow outbound communication with storage over HTTPS'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: 'Storage.ukwest'
access: 'Allow'
priority: 106
direction: 'Outbound'
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-optional-azure-out-${subnetPrefixString}-v11'
properties: {
description: 'Allow AzureCloud outbound https traffic'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: subnetPrefix
destinationAddressPrefix: 'AzureCloud'
access: 'Allow'
priority: 107
direction: 'Outbound'
}
}
]
}
}
module route 'modules/route-table/main.bicep' = {
name: '${uniqueString(deployment().name, location)}-rt'
params: {
name: rtName
location: location
routes: [
{
name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${subnetPrefixString}-to-vnetlocal'
properties: {
addressPrefix: subnetPrefix
nextHopType: 'VnetLocal'
hasBgpOverride: false
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory'
properties: {
addressPrefix: 'AzureActiveDirectory'
nextHopType: 'Internet'
hasBgpOverride: false
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-OneDsCollector'
properties: {
addressPrefix: 'OneDsCollector'
nextHopType: 'Internet'
hasBgpOverride: false
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.uksouth'
properties: {
addressPrefix: 'Storage.uksouth'
nextHopType: 'Internet'
hasBgpOverride: false
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.ukwest'
properties: {
addressPrefix: 'Storage.ukwest'
nextHopType: 'Internet'
hasBgpOverride: false
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_optional-AzureCloud.uksouth'
properties: {
addressPrefix: 'AzureCloud.uksouth'
nextHopType: 'Internet'
hasBgpOverride: false
}
}
{
name: 'Microsoft.Sql-managedInstances_UseOnly_optional-AzureCloud.ukwest'
properties: {
addressPrefix: 'AzureCloud.ukwest'
nextHopType: 'Internet'
hasBgpOverride: false
}
}
]
}
}
view raw main.bicep hosted with ❤ by GitHub

These networking objects have been created based on what the current architecture requires for the security rules & routes, which sets out the mandatory rules and routes required for Azure SQL Managed Instance to work properly.

I also stumbled across others having a similar problem with ARM/Bicep in Azure Databricks which would require the same solution by defining the necessary rules and routes Can’t redeploy Network Security Group for Databricks VNet injection · Issue #6670 · Azure/azure-quickstart-templates · GitHub.

Alternatively, after the initial Azure SQL Managed Instance deployment, go to your Network Security Group and Route table, export to template and convert to Bicep using the decompile command (or if you have the VSCode extension, it should auto-convert on paste!): az bicep decompile --file main.json

What’s your thoughts?

Hopefully this saves others some time when trying to create their Azure Bicep SQL Managed Instance templates! There is a GitHub issue open which seems like others are hitting the same hurdle too:

Resource types for Network Intent Policies · Issue #1755 · Azure/bicep-types-az · GitHub

Hopefully this helps someone else out there who comes across this issue. Let me know in the comments if you had a similar problem.

Leave a comment


Skip to content