Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Updated Network Manager module to support Routing Configuration feature #4096

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
582 changes: 517 additions & 65 deletions avm/res/network/network-manager/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Connectivity configurations define hub-and-spoke or mesh topologies applied to o

| Resource Type | API Version |
| :-- | :-- |
| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkManagers/connectivityConfigurations) |
| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2024-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2024-05-01/networkManagers/connectivityConfigurations) |

## Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ param deleteExistingPeering bool = false
@sys.description('Optional. Flag if global mesh is supported. By default, mesh connectivity is applied to virtual networks within the same region. If set to "True", a global mesh enables connectivity across regions.')
param isGlobal bool = false

resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' existing = {
resource networkManager 'Microsoft.Network/networkManagers@2024-05-01' existing = {
name: networkManagerName
}

resource connectivityConfiguration 'Microsoft.Network/networkManagers/connectivityConfigurations@2023-11-01' = {
resource connectivityConfiguration 'Microsoft.Network/networkManagers/connectivityConfigurations@2024-05-01' = {
name: name
parent: networkManager
properties: {
Expand Down Expand Up @@ -68,6 +68,7 @@ output resourceGroupName string = resourceGroup().name
// Definitions //
// =============== //

@export()
type appliesToGroupsType = {
@sys.description('Required. Group connectivity type.')
groupConnectivity: ('DirectlyConnected' | 'None')
Expand All @@ -82,6 +83,7 @@ type appliesToGroupsType = {
useHubGateway: bool?
}[]

@export()
type hubsType = {
@sys.description('Required. Resource Id of the hub.')
resourceId: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "0.32.4.45862",
"templateHash": "9454472323100733583"
"templateHash": "12807816660171449397"
},
"name": "Network Manager Connectivity Configurations",
"description": "This module deploys a Network Manager Connectivity Configuration.\nConnectivity configurations define hub-and-spoke or mesh topologies applied to one or more network groups.",
Expand Down Expand Up @@ -49,6 +49,9 @@
}
}
}
},
"metadata": {
"__bicep_export!": true
}
},
"hubsType": {
Expand All @@ -73,7 +76,10 @@
}
}
},
"nullable": true
"nullable": true,
"metadata": {
"__bicep_export!": true
}
}
},
"parameters": {
Expand Down Expand Up @@ -139,12 +145,12 @@
"networkManager": {
"existing": true,
"type": "Microsoft.Network/networkManagers",
"apiVersion": "2023-11-01",
"apiVersion": "2024-05-01",
"name": "[parameters('networkManagerName')]"
},
"connectivityConfiguration": {
"type": "Microsoft.Network/networkManagers/connectivityConfigurations",
"apiVersion": "2023-11-01",
"apiVersion": "2024-05-01",
"name": "[format('{0}/{1}', parameters('networkManagerName'), parameters('name'))]",
"properties": {
"appliesToGroups": "[map(parameters('appliesToGroups'), lambda('group', createObject('groupConnectivity', lambdaVariables('group').groupConnectivity, 'isGlobal', coalesce(string(lambdaVariables('group').isGlobal), 'false'), 'networkGroupId', lambdaVariables('group').networkGroupResourceId, 'useHubGateway', coalesce(string(lambdaVariables('group').useHubGateway), 'false'))))]",
Expand Down
178 changes: 49 additions & 129 deletions avm/res/network/network-manager/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ param name string
@sys.description('Optional. Location for all resources.')
param location string = resourceGroup().location

import { lockType } from 'br/public:avm/utl/types/avm-common-types:0.4.1'
@sys.description('Optional. The lock settings of the service.')
param lock lockType
param lock lockType?

import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.4.1'
@sys.description('Optional. Array of role assignments to create.')
param roleAssignments roleAssignmentType
param roleAssignments roleAssignmentType[]?

@sys.description('Optional. Tags of the resource.')
param tags object?
Expand All @@ -23,7 +25,7 @@ param tags object?
@sys.description('Optional. A description of the Network Manager.')
param description string = ''

@sys.description('Required. Scope Access. String array containing any of "Connectivity", "SecurityAdmin". The connectivity feature allows you to create network topologies at scale. The security admin feature lets you create high-priority security rules, which take precedence over NSGs.')
@sys.description('Optional. Scope Access (Also known as features). String array containing any of "Connectivity", "SecurityAdmin", or "Routing". The connectivity feature allows you to create network topologies at scale. The security admin feature lets you create high-priority security rules, which take precedence over NSGs. The routing feature allows you to describe your desired routing behavior and orchestrate user-defined routes (UDRs) to create and maintain the desired routing behavior. If none of the features are required, then this parameter does not need to be specified, which then only enables features like "IPAM" and "Virtual Network Verifier".')
param networkManagerScopeAccesses networkManagerScopeAccessType

@sys.description('Required. Scope of Network Manager. Contains a list of management groups or a list of subscriptions. This defines the boundary of network resources that this Network Manager instance can manage. If using Management Groups, ensure that the "Microsoft.Network" resource provider is registered for those Management Groups prior to deployment. Must contain at least one management group or subscription.')
Expand All @@ -38,15 +40,18 @@ param connectivityConfigurations connectivityConfigurationsType
@sys.description('Optional. Scope Connections to create for the network manager. Allows network manager to manage resources from another tenant. Supports management groups or subscriptions from another tenant.')
param scopeConnections scopeConnectionType

@sys.description('Optional. Security Admin Configurations, Rule Collections and Rules to create for the network manager. Azure Virtual Network Manager provides two different types of configurations you can deploy across your virtual networks, one of them being a SecurityAdmin configuration. A security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. You then associate the rule collection with the network groups that you want to apply the security admin rules to.')
@sys.description('Optional. Security Admin Configurations requires enabling the "SecurityAdmin" feature on Network Manager. A security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. You then associate the rule collection with the network groups that you want to apply the security admin rules to.')
param securityAdminConfigurations securityAdminConfigurationsType

@sys.description('Optional. Routing Configurations requires enabling the "Routing" feature on Network Manager. A routing configuration contains a set of rule collections. Each rule collection contains one or more routing rules.')
param routingConfigurations routingConfigurationsType

@sys.description('Optional. Enable/Disable usage telemetry for module.')
param enableTelemetry bool = true

var builtInRoleNames = {
Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
'IPAM Pool Contributor': subscriptionResourceId(
'IPAM Pool User': subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
'7b3e853f-ad5d-4fb5-a7b8-56a3581c7037'
)
Expand Down Expand Up @@ -104,7 +109,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT
}
}

resource networkManager 'Microsoft.Network/networkManagers@2023-11-01' = {
resource networkManager 'Microsoft.Network/networkManagers@2024-05-01' = {
name: name
location: location
tags: tags
Expand All @@ -123,6 +128,7 @@ module networkManager_networkGroups 'network-group/main.bicep' = [
networkManagerName: networkManager.name
description: networkGroup.?description ?? ''
staticMembers: networkGroup.?staticMembers
memberType: networkGroup.?memberType ?? 'VirtualNetwork'
}
}
]
Expand Down Expand Up @@ -171,6 +177,19 @@ module networkManager_securityAdminConfigurations 'security-admin-configuration/
}
]

module networkManager_routingConfigurations 'routing-configuration/main.bicep' = [
for (routingConfiguration, index) in routingConfigurations ?? []: {
name: '${uniqueString(deployment().name, location)}-NetworkManager-RoutingConfigurations-${index}'
params: {
name: routingConfiguration.name
networkManagerName: networkManager.name
description: routingConfiguration.?description ?? ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the fallback defaults here intended? If not provided for a nullable parameter they just won't be used. In other words: I'd only recommend setting a fallback default here if you definitely want to not pass null.

ruleCollections: routingConfiguration.?ruleCollections ?? []
}
dependsOn: networkManager_networkGroups
}
]

resource networkManager_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
name: lock.?name ?? 'lock-${name}'
properties: {
Expand Down Expand Up @@ -214,58 +233,22 @@ output location string = networkManager.location
// Definitions //
// =============== //

type lockType = {
@sys.description('Optional. Specify the name of lock.')
name: string?

@sys.description('Optional. Specify the type of lock.')
kind: ('CanNotDelete' | 'ReadOnly' | 'None')?
}?

type roleAssignmentType = {
@sys.description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.')
name: string?

@sys.description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.')
roleDefinitionIdOrName: string

@sys.description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
principalId: string

@sys.description('Optional. The principal type of the assigned principal ID.')
principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?

@sys.description('Optional. The description of the role assignment.')
description: string?

@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".')
condition: string?

@sys.description('Optional. Version of the condition.')
conditionVersion: '2.0'?

@sys.description('Optional. The Resource Id of the delegated managed identity resource.')
delegatedManagedIdentityResourceId: string?
}[]?

import { staticMembersType } from 'network-group/main.bicep'
type networkGroupType = {
@sys.description('Required. The name of the network group.')
name: string

@sys.description('Optional. A description of the network group.')
description: string?

@sys.description('Optional. Static Members to create for the network group. Contains virtual networks to add to the network group.')
staticMembers: {
@sys.description('Required. The name of the static member.')
name: string
@sys.description('Optional. The type of the group member. Subnet member type is used for routing configurations.')
memberType: ('Subnet' | 'VirtualNetwork')?

@sys.description('Required. Resource ID of the virtual network.')
resourceId: string
}[]?
@sys.description('Optional. Static Members to create for the network group. Contains virtual networks to add to the network group.')
staticMembers: staticMembersType?
}[]?

type networkManagerScopeAccessType = ('Connectivity' | 'SecurityAdmin')[]
type networkManagerScopeAccessType = ('Connectivity' | 'SecurityAdmin' | 'Routing')[]?
Copy link
Contributor

@AlexanderSehr AlexanderSehr Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type networkManagerScopeAccessType = ('Connectivity' | 'SecurityAdmin' | 'Routing')[]?
type networkManagerScopeAccessType = ('Connectivity' | 'SecurityAdmin' | 'Routing')

As per soon to be released specs, both the array and nullable indicators should be set at the parameter, not the UDT-side. The reasoning is simply to make it easier to understand a parameter/property by looking at it.

So this change should go along with updating the parameter to

param networkManagerScopeAccesses networkManagerScopeAccessType[]?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The same idea applies to all other types


type networkManagerScopesType = {
@sys.description('Conditional. List of fully qualified IDs of management groups to assign to the network manager to manage. Required if `subscriptions` is not provided. Fully qualified ID format: \'/providers/Microsoft.Management/managementGroups/{managementGroupId}\'.')
Expand All @@ -289,6 +272,7 @@ type scopeConnectionType = {
tenantId: string
}[]?

import { appliesToGroupsType, hubsType } from 'connectivity-configuration/main.bicep'
type connectivityConfigurationsType = {
@sys.description('Required. The name of the connectivity configuration.')
name: string
Expand All @@ -297,31 +281,13 @@ type connectivityConfigurationsType = {
description: string?

@sys.description('Required. Network Groups for the configuration. A connectivity configuration must be associated to at least one network group.')
appliesToGroups: {
@sys.description('Required. Group connectivity type.')
groupConnectivity: ('DirectlyConnected' | 'None')

@sys.description('Optional. Flag if global is supported.')
isGlobal: bool?

@sys.description('Required. Resource Id of the network group.')
networkGroupResourceId: string

@sys.description('Optional. Flag if use hub gateway.')
useHubGateway: bool?
}[]
appliesToGroups: appliesToGroupsType

@sys.description('Required. The connectivity topology to apply the configuration to.')
connectivityTopology: ('HubAndSpoke' | 'Mesh')

@sys.description('Optional. The hubs to apply the configuration to.')
hubs: {
@sys.description('Required. Resource Id of the hub.')
resourceId: string

@sys.description('Required. Resource type of the hub.')
resourceType: 'Microsoft.Network/virtualNetworks'
}[]?
hubs: hubsType?

@sys.description('Optional. Delete existing peering connections.')
deleteExistingPeering: bool?
Expand All @@ -330,6 +296,7 @@ type connectivityConfigurationsType = {
isGlobal: bool?
}[]?

import { applyOnNetworkIntentPolicyBasedServicesType, securityAdminConfigurationRuleCollectionType } from 'security-admin-configuration/main.bicep'
type securityAdminConfigurationsType = {
@sys.description('Required. The name of the security admin configuration.')
name: string
Expand All @@ -338,67 +305,20 @@ type securityAdminConfigurationsType = {
description: string?

@sys.description('Required. Apply on network intent policy based services.')
applyOnNetworkIntentPolicyBasedServices: ('None' | 'All' | 'AllowRulesOnly')[]
applyOnNetworkIntentPolicyBasedServices: applyOnNetworkIntentPolicyBasedServicesType

@sys.description('Optional. Rule collections to create for the security admin configuration.')
ruleCollections: {
@sys.description('Required. The name of the admin rule collection.')
name: string

@sys.description('Optional. A description of the admin rule collection.')
description: string?

@sys.description('Required. List of network groups for configuration. An admin rule collection must be associated to at least one network group.')
appliesToGroups: {
@sys.description('Required. The resource ID of the network group.')
networkGroupResourceId: string
}[]

@sys.description('Optional. List of rules for the admin rules collection. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.')
rules: {
@sys.description('Required. The name of the rule.')
name: string

@sys.description('Required. Indicates the access allowed for this particular rule. "Allow" means traffic matching this rule will be allowed. "Deny" means traffic matching this rule will be blocked. "AlwaysAllow" means that traffic matching this rule will be allowed regardless of other rules with lower priority or user-defined NSGs.')
access: 'Allow' | 'AlwaysAllow' | 'Deny'

@sys.description('Optional. A description of the rule.')
description: string?

@sys.description('Optional. List of destination port ranges. This specifies on which ports traffic will be allowed or denied by this rule. Provide an (*) to allow traffic on any port. Port ranges are between 1-65535.')
destinationPortRanges: string[]?

@sys.description('Optional. The destnations filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted.')
destinations: {
@sys.description('Required. Address prefix type.')
addressPrefixType: 'IPPrefix' | 'ServiceTag'

@sys.description('Required. Address prefix.')
addressPrefix: string
}[]?

@sys.description('Required. Indicates if the traffic matched against the rule in inbound or outbound.')
direction: 'Inbound' | 'Outbound'

@minValue(1)
@maxValue(4096)
@sys.description('Required. The priority of the rule. The value can be between 1 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule.')
priority: int

@sys.description('Required. Network protocol this rule applies to.')
protocol: 'Ah' | 'Any' | 'Esp' | 'Icmp' | 'Tcp' | 'Udp'

@sys.description('Optional. List of destination port ranges. This specifies on which ports traffic will be allowed or denied by this rule. Provide an (*) to allow traffic on any port. Port ranges are between 1-65535.')
sourcePortRanges: string[]?

@sys.description('Optional. The source filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted.')
sources: {
@sys.description('Required. Address prefix type.')
addressPrefixType: 'IPPrefix' | 'ServiceTag'

@sys.description('Required. Address prefix.')
addressPrefix: string
}[]?
}[]?
}[]?
ruleCollections: securityAdminConfigurationRuleCollectionType?
}[]?

import { routingConfigurationRuleCollectionType } from 'routing-configuration/main.bicep'
type routingConfigurationsType = {
@sys.description('Required. The name of the routing configuration.')
name: string

@sys.description('Optional. A description of the routing configuration.')
description: string?

@sys.description('Optional. Rule collections to create for the routing configuration.')
ruleCollections: routingConfigurationRuleCollectionType?
}[]?
Loading
Loading