From 7a95a83d02f604af52067d14b1d7818581428406 Mon Sep 17 00:00:00 2001 From: tulpy Date: Mon, 26 Jan 2026 21:13:37 +0800 Subject: [PATCH] ipam modules and templates --- avnm/avnm.bicep | 127 ++++ avnm/configuration/.DS_Store | Bin 0 -> 6148 bytes .../platformConnectivity-avnm.bicepparam | 40 ++ avnm/configuration/shared/lz.type.bicep | 645 ++++++++++++++++++ avnm/ipam.bicep | 74 ++ avnm/ipamPerRegion.bicep | 119 ++++ 6 files changed, 1005 insertions(+) create mode 100644 avnm/avnm.bicep create mode 100644 avnm/configuration/.DS_Store create mode 100644 avnm/configuration/platform/platformConnectivity-avnm.bicepparam create mode 100644 avnm/configuration/shared/lz.type.bicep create mode 100644 avnm/ipam.bicep create mode 100644 avnm/ipamPerRegion.bicep diff --git a/avnm/avnm.bicep b/avnm/avnm.bicep new file mode 100644 index 0000000..bc48bce --- /dev/null +++ b/avnm/avnm.bicep @@ -0,0 +1,127 @@ +import { + // Base Landing Zone User Defined Types + tagsType +} from './configuration/shared/lz.type.bicep' + +targetScope = 'resourceGroup' + +metadata name = 'Azure Virtual Network Manager with IPAM' +metadata description = 'Azure Virtual Network Manager with IPAM Module.' +metadata version = '1.0.0' +metadata author = 'Insight APAC Platform Engineering' + +@description('Optional. Location for the resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags tagsType? + +@description('Required. Configuration for the Azure Virtual Network Manager.') +param avnmConfiguration avnmType + +@description('Required. List of regions for the Ipam Pool.') +param regions ipamRegionType + +@description('Required. Set to true to deploy IPAM resources, false to only generate outputs.') +param deploy bool + +@description('Resource. Azure Virtual Network Manager.') +module networkManagers 'br/public:avm/res/network/network-manager:0.5.3' = if (deploy) { + params: { + location: location + name: avnmConfiguration.name + networkManagerScopes: { + managementGroups: avnmConfiguration.?managementGroupScopes + subscriptions: avnmConfiguration.?subscriptionScopes + } + tags: tags + } +} + +@description('Module: IPAM') +module ipam './ipam.bicep' = if (deploy) { + params: { + avnmConfiguration: avnmConfiguration + avnmName: networkManagers.?outputs.name ?? '' + deploy: deploy + location: location + regions: regions + tags: tags + } +} + +// Outputs +@description('The resource ID of the network manager.') +output avnmNameResourceId string = networkManagers.?outputs.resourceId ?? '' + +@description('The name of the network manager.') +output avnmName string = networkManagers.?outputs.name ?? '' + +// User Defined Types +import { networkGroupType, securityAdminConfigurationType } from 'br/public:avm/res/network/network-manager:0.5.3' +type avnmType = { + @minLength(1) + @maxLength(64) + @description('Required. Name of the Network Manager.') + name: string + + @description('Optional. Subscription scopes for the Network Manager.') + subscriptionScopes: string[]? + + @description('Optional. Management group scopes for the Network Manager.') + managementGroupScopes: string[]? + + @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.') + securityAdminConfigurations: securityAdminConfigurationType[]? + + @description('Conditional. Network Groups and static members to create for the network manager. Required if using "connectivityConfigurations" or "securityAdminConfigurations" parameters. A network group is global container that includes a set of virtual network resources from any region. Then, configurations are applied to target the network group, which applies the configuration to all members of the group. The two types are group memberships are static and dynamic memberships. Static membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks, and is available as a child module, while dynamic membership is defined through Azure policy. See [How Azure Policy works with Network Groups](https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-azure-policy-integration) for more details.') + networkGroups: networkGroupType[]? + + @description('Required. IPAM Root Settings.') + ipamRootSettings: ipamRootType +} + +type ipamRootType = { + @minLength(1) + @maxLength(64) + @description('Required. Name of the root IPAM pool.') + rootIpamPoolName: string + + @minLength(9) + @maxLength(18) + @description('Required. CIDR block for the Azure Supernet.') + azureCidr: string + + @maxValue(32) + @minValue(8) + @description('Required. CIDR size for the region IPAM pools.') + regionCidrSize: int + + @maxValue(32) + @minValue(8) + @description('Required. CIDR split size for the region IPAM pools.') + regionLzCidrSize: int +} + +@maxLength(16) +type ipamRegionType = { + @minLength(1) + @maxLength(64) + @description('Required. Name of the Azure region.') + name: string + + @minLength(1) + @maxLength(256) + @description('Required. Display name of the Azureregion.') + displayName: string + + @maxValue(100) + @minValue(0) + @description('Required. Factor to divide the Azureregion CIDR into platform and application landing zones, in percentage.') + platformAndApplicationSplitFactor: int + + @minLength(9) + @maxLength(18) + @description('Required. CIDR block for the Azure region.') + cidr: string +}[] diff --git a/avnm/configuration/.DS_Store b/avnm/configuration/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7c891dc191a67ab6d960a91abf5d29133010bf90 GIT binary patch literal 6148 zcmeHKy-ve05Wa&VMPlj5=vU~(8mcfNA@v0yjesGMqtfo4c`#VnS$Gt_^Bt&BJs<{z z(4Ay|XWx(GPm1j$B3|yg1<{;{Dl|bBr64k0G##1q36OJ+JBqZSp?h5Sg+zaGO1{0K zk$SqNE$w}O8)M$Xfw8||tk!WDbvIAvr36E4nzoOv*sfo%-k!&oi?Uz$Ex++$f6aHK za&ZQn0cXG&a0Y(L0Pbv&>QvEtXTTY72EG`O^C6%K)`m$jA06m&2>_g7Itz5EB_t*o z)`m$D76@x7P(#^D4AyYW2lJ~9lcI(bTk*lR@@MhFd3EFu*`2sn^xhe82Br)g>u@gj z|0RBzVv#=$@sTs&4E!?&c-E|%B{pSu>%sQqt_^56Xd>d5M1eq`JOVI~bL1==)gDBL YUu~EaC5z1GaG*Z~G9lhM1HZt)2ZN$H8vp.tags? + + @description('Optional. The lock settings of the service.') + lock: lockType? + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType[]? +} + +// 1.4 Azure Budget Type +@export() +@description('The type for Azure Budgets') +type budgetType = { + budgets: { + @minLength(1) + @maxLength(63) + @description('Required. The name of the budget.') + name: string + + @description('Optional. The category of the budget, whether the budget tracks cost or usage.') + category: ('Cost' | 'Usage')? + + @description('Optional. The start date for the budget. Start date should be the first day of the month and cannot be in the past (except for the current month).') + startDate: string? + + @description('Required. The total amount of cost or usage to track with the budget.') + amount: int + + @description('Optional. The type of threshold to use for the budget. The threshold type can be either `Actual` or `Forecasted`.') + thresholdType: ('Actual' | 'Forecasted')? + + @maxLength(5) + @description('Optional. Percent thresholds of budget for when to get a notification. Can be up to 5 thresholds, where each must be between 1 and 1000.') + thresholds: array? + + @description('Conditional. The list of email addresses to send the budget notification to when the thresholds are exceeded. Required if neither `contactRoles` nor `actionGroups` was provided.') + contactEmails: array? + }[] +} + +// 1.5 Role Assignment Types +@export() +@description('The type for Role Assignment configuration.') +type roleAssignmentsType = roleAssignments[]? + +@description('The type for Role Assignments.') +type roleAssignments = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + + @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 + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @maxLength(36) + @description('Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') + subscriptionId: string? + + @description('Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group.') + resourceGroupName: string? + + @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? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +} + +// 1.6 Privileged Identity Management Role Assignment Types +import { + requestTypeType + ticketInfoType +} from 'br/public:avm/ptn/authorization/pim-role-assignment:0.1.2' +@export() +@description('The type for Privileged Identity Management role assignment configuration.') +type pimRoleAssignmentsType = pimRoleAssignments[]? + +@description('The type for Privileged Identity Management role assignment.') +type pimRoleAssignments = { + @description('Principal (user or service principal) object ID.') + principalId: string + + @description('Required. You can provide either the display name of the role definition (must be configured in the variable `builtInRoleNames`), or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Optional. Name of the Resource Group to assign the RBAC role to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided RBAC role to the resource group.') + resourceGroupName: string? + + @description('Optional. Subscription ID of the subscription to assign the RBAC role to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided RBAC role to the subscription.') + subscriptionId: string? + + @description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') + managementGroupId: string? + + @description('Required. The type of the PIM role assignment whether its active or eligible.') + pimRoleAssignmentType: object + + @description('Optional. The justification for the role eligibility.') + justification: string? + + @description('Required. The type of the role assignment eligibility request.') + requestType: requestTypeType + + @description('Optional. The resultant role eligibility assignment id or the role eligibility assignment id being updated.') + targetRoleEligibilityScheduleId: string? + + @description('Optional. The role eligibility assignment instance id being updated.') + targetRoleEligibilityScheduleInstanceId: string? + + @description('Optional. The resultant role assignment schedule id or the role assignment schedule id being updated.') + targetRoleAssignmentScheduleId: string? + + @description('Optional. The role assignment schedule instance id being updated.') + targetRoleAssignmentScheduleInstanceId: string? + + @description('Optional. Ticket Info of the role eligibility.') + ticketInfo: ticketInfoType? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') + condition: string? + + @description('Optional. Version of the condition. Currently accepted value is "2.0".') + conditionVersion: ('2.0')? +} + +// 1.7 Azure Storage Account Type +import { + networkAclsType as storageNetworkAclsType + secretsExportConfigurationType as storageSecretsExportConfigurationType + localUserType +} from 'br/public:avm/res/storage/storage-account:0.30.0' +@export() +@description('The type for Azure Storage Account configuration.') +type storageAccountType = { + @maxLength(24) + @description('Name of the Storage Account. Must be lower-case.') + name: string? + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType[]? + + @description('Optional. The lock settings of the service.') + lock: lockType? + + @description('Optional. Type of Storage Account to create.') + kind: 'StorageV2' | 'Storage' | 'BlobStorage' | 'FileStorage' | 'BlockBlobStorage'? + + @description('Optional. Storage account SKU. Defaults to \'Standard_ZRS\'.') + sku: + | 'Standard_LRS' + | 'Standard_ZRS' + | 'Standard_GRS' + | 'Standard_GZRS' + | 'Standard_RAGRS' + | 'Standard_RAGZRS' + | 'StandardV2_LRS' + | 'StandardV2_ZRS' + | 'StandardV2_GRS' + | 'StandardV2_GZRS' + | 'Premium_LRS' + | 'Premium_ZRS' + | 'PremiumV2_LRS' + | 'PremiumV2_ZRS' + + @description('Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The "Premium" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type.') + accessTier: 'Hot' | 'Cool' | 'Cold' | 'Premium' + + @description('Optional. Allow large file shares if set to \'Enabled\'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares).') + largeFileSharesState: 'Disabled' | 'Enabled'? + + @description('Optional. Provides the identity based authentication settings for Azure Files.') + azureFilesIdentityBasedAuthentication: resourceInput<'Microsoft.Storage/storageAccounts@2024-01-01'>.properties.azureFilesIdentityBasedAuthentication? + + @description('Optional. A boolean flag which indicates whether the default authentication is OAuth or not.') + defaultToOAuthAuthentication: bool? + + @description('Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true.') + allowSharedKeyAccess: bool? + + @description('Optional. The Storage Account ManagementPolicies Rules.') + managementPolicyRules: array? + + @description('Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny.') + networkAcls: storageNetworkAclsType? + + @description('Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true.') + requireInfrastructureEncryption: bool? + + @description('Optional. Allow or disallow cross AAD tenant object replication.') + allowCrossTenantReplication: bool? + + @description('Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source.') + customDomainName: string? + + @description('Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates.') + customDomainUseSubDomainName: bool? + + @description('Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier.') + dnsEndpointType: 'AzureDnsZone' | 'Standard'? + + @description('Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false.') + allowBlobPublicAccess: bool? + + @description('Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore.') + minimumTlsVersion: 'TLS1_2' + + @description('Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true.') + enableHierarchicalNamespace: bool? + + @description('Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true.') + enableSftp: bool? + + @description('Optional. Local users to deploy for SFTP authentication.') + localUsers: localUserType[]? + + @description('Optional. Enables local users feature, if set to true.') + isLocalUserEnabled: bool? + + @description('Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true.') + enableNfsV3: bool? + + @description('Optional. Tags of the resource.') + tags: resourceInput<'Microsoft.Storage/storageAccounts@2024-01-01'>.tags? + + @description('Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet.') + allowedCopyScope: 'AAD' | 'PrivateLink'? + + @description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set.') + publicNetworkAccess: 'Enabled' | 'Disabled'? + + @description('Optional. Allows HTTPS traffic only to storage service if sets to true.') + supportsHttpsTrafficOnly: bool? + + @description('Optional. The SAS expiration period. DD.HH:MM:SS.') + sasExpirationPeriod: string? + + @description('Optional. The SAS expiration action. Allowed values are Block and Log.') + sasExpirationAction: 'Log' | 'Block'? + + @description('Optional. The keyType to use with Queue & Table services.') + keyType: 'Account' | 'Service'? + + @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') + secretsExportConfiguration: storageSecretsExportConfigurationType? +} + +// 1.8 Azure Key Vault Type +import { + networkAclsType as keyVaultNetworkAclsType + accessPolicyType + secretType +} from 'br/public:avm/res/key-vault/vault:0.13.3' +@export() +@description('The type for Azure Key Vault configuration.') +type keyVaultType = { + @minLength(3) + @maxLength(24) + @description('Optional. Name of the Azure Key Vault. Must be globally unique.') + name: string? + + @description('Optional. Specifies the SKU for the vault.') + sku: ('standard' | 'premium')? + + @description('Optional. Switch to enable/disable Key Vault\'s soft delete feature.') + enableSoftDelete: bool? + + @description('Optional. Property that controls how data actions are authorized. When true, the key vault will use Role Based Access Control (RBAC) for authorization of data actions, and the access policies specified in vault properties will be ignored. When false, the key vault will use the access policies specified in vault properties, and any policy stored on Azure Resource Manager will be ignored. Note that management actions are always authorized with RBAC.') + enableRbacAuthorization: bool? + + @description('Optional. The vault\'s create mode to indicate whether the vault need to be recovered or not. - recover or default.') + createMode: ('default' | 'recover')? + + @minValue(7) + @maxValue(90) + @description('Optional. softDelete data retention days. It accepts >=7 and <=90.') + softDeleteRetentionInDays: int? + + @description('Optional. Provide \'true\' to enable Key Vault\'s purge protection feature.') + enablePurgeProtection: bool? + + @description('Optional. This value can be set to \'Enabled\' to avoid breaking changes on existing customer resources and templates. If set to \'Disabled\', traffic over public interface is not allowed, and private endpoint connections would be the exclusive access method.') + publicNetworkAccess: ('Enabled' | 'Disabled' | '')? + + @description('Optional. All access policies to create.') + accessPolicies: accessPolicyType[]? + + @description('Optional. All secrets to create.') + secrets: secretType[]? + + @description('Optional. Rules governing the accessibility of the resource from specific network locations.') + networkAcls: keyVaultNetworkAclsType? + + @description('Optional. Resource tags.') + tags: resourceInput<'Microsoft.KeyVault/vaults@2024-11-01'>.tags? + + @description('Optional. The lock settings of the service.') + lock: lockType? + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType[]? +} + +// 1.9 Azure User Assigned Identity Type +@export() +@description('The type for Azure User Assigned Identity configuration.') +type userAssignedIdentityType = { + @minLength(5) + @maxLength(128) + @description('Optional. The name of the User Assigned Identity.') + name: string? + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType[]? + + @description('Optional. The lock settings of the service.') + lock: lockType? + + @description('Required. The name of the resource group containing the user assigned identity.') + resourceGroupName: string + + @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 +} diff --git a/avnm/ipam.bicep b/avnm/ipam.bicep new file mode 100644 index 0000000..2533b60 --- /dev/null +++ b/avnm/ipam.bicep @@ -0,0 +1,74 @@ +@description('Required. Location for the resources.') +param location string + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Required. Configuration for the Ipam AVNM.') +param avnmConfiguration object + +@description('Required. List of regions for the Ipam Pool.') +param regions array + +@description('Required. Set to true to deploy IPAM resources, false to only generate outputs.') +param deploy bool + +@description('Required. Name of the existing Azure Virtual Network Manager.') +param avnmName string + +@description('Resource: Existing Azure Virtual Network Manager.') +resource avnm 'Microsoft.Network/networkManagers@2024-05-01' existing = if (deploy) { + name: avnmName +} + +@description('Resource: Root IPAM Pool for the Azure Supernet.') +resource rootIpamPool 'Microsoft.Network/networkManagers/ipamPools@2024-05-01' = if (deploy) { + name: 'rootIpamPool-${replace(avnmConfiguration.ipamRootSettings.azureCidr, '/', '-')}' + parent: avnm + location: location + tags: tags + properties: { + addressPrefixes: [ + avnmConfiguration.ipamRootSettings.azureCidr + ] + displayName: avnmConfiguration.ipamRootSettings.rootIpamPoolName + description: 'Root IPAM pool for the Azure Supernet - (${avnmConfiguration.ipamRootSettings.azureCidr})' + } +} + +@description('Module: IPAM Pool for each region.') +module regionIpamPool './ipamPerRegion.bicep' = [ + for region in regions: { + name: 'regionIpamPool-${region.name}' + params: { + avnmName: avnmName + deploy: deploy + rootIPAMpoolName: 'rootIpamPool-${replace(avnmConfiguration.ipamRootSettings.azureCidr, '/', '-')}' + regionDisplayName: region.displayName + regionCidr: region.cidr + regionLzCidrSize: avnmConfiguration.ipamRootSettings.regionLzCidrSize + platformAndApplicationSplitFactor: region.platformAndApplicationSplitFactor + location: region.name + tags: tags + } + } +] + +// Outputs +@description('Azure Supernet CIDR.') +output AzureCIDR string = avnmConfiguration.ipamRootSettings.azureCidr + +@description('Region Ipam Pools.') +output regionIpamPools array = [ + for (region, i) in regions: { + Region: region.displayName + value: { + name: region.name + regionCidr: region.cidr + applicationCidrs: regionIpamPool[i].outputs.applicationCIDRs + applicationCidrsCount: regionIpamPool[i].outputs.applicationCIDRsCount + platformCidrs: regionIpamPool[i].outputs.platformCIDRs + platformCidrsCount: regionIpamPool[i].outputs.platformCIDRs + } + } +] diff --git a/avnm/ipamPerRegion.bicep b/avnm/ipamPerRegion.bicep new file mode 100644 index 0000000..de01af0 --- /dev/null +++ b/avnm/ipamPerRegion.bicep @@ -0,0 +1,119 @@ +@description('Required. Location for the resources.') +param location string + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Required. Display name of the region.') +param regionDisplayName string + +@description('Required. CIDR for the region IPAM pool.') +param regionCidr string + +@description('Required. Name of the root IPAM pool.') +param rootIPAMpoolName string + +@description('Required. Name of the existing Azure Virtual Network Manager.') +param avnmName string + +@description('Required. Set to true to deploy IPAM resources, false to only generate outputs.') +param deploy bool + +@maxValue(32) +@minValue(8) +@description('CIDR size for the region IPAM pool. This is used to determine how many subnets can be created within the region CIDR.') +param regionLzCidrSize int + +@maxValue(100) +@minValue(0) +@description('Factor to divide the region CIDR into platform and application landing zones, in percentage.') +param platformAndApplicationSplitFactor int + +// Calculate the total number of CIDR blocks available in the region +// Using the RegionCIDRsplitSize parameter to determine subdivision granularity +var powersOfTwo = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] +var currentRegionCidrSplitSize = int(split(regionCidr, '/')[1]) +var subdivisionSize = regionLzCidrSize // Use the parameter directly for subdivision +var totalSubnetCount = powersOfTwo[subdivisionSize - currentRegionCidrSplitSize] + +// Generate all possible CIDR blocks for allocation +var allSubnets = [for i in range(0, totalSubnetCount): cidrSubnet(regionCidr, subdivisionSize, i)] + +// Calculate how many subnets go to platform vs application based on the factor +var platformSubnetCount = max(1, totalSubnetCount * platformAndApplicationSplitFactor / 100) +var platformSubnets = take(allSubnets, platformSubnetCount) +var applicationSubnets = skip(allSubnets, platformSubnetCount) + +// Calculate the CIDRs for application landing zones +var platformCIDRs = platformSubnets + +// Calculate the CIDRs for application landing zones +var applicationCIDRs = applicationSubnets + +@description('Resource: Existing Azure Virtual Network Manager.') +resource avnm 'Microsoft.Network/networkManagers@2024-05-01' existing = if (deploy) { + name: avnmName +} + +@description('Resource: Region IPAM Pool.') +resource regionIpamPool 'Microsoft.Network/networkManagers/ipamPools@2024-05-01' = if (deploy) { + name: 'regionIpamPool-${replace(location, ' ', '-')}-${replace(regionCidr, '/', '-')}' + parent: avnm + location: location + tags: tags + properties: { + addressPrefixes: [ + regionCidr + ] + parentPoolName: rootIPAMpoolName + displayName: regionDisplayName + description: 'IPAM pool for ${regionDisplayName} region (${regionCidr})' + } +} + +@description('Resource: Platform Landing Zone IPAM Pool.') +resource platformIpamPool 'Microsoft.Network/networkManagers/ipamPools@2024-05-01' = if (deploy) { + name: 'platformIpamPool-${replace(location, ' ', '-')}' + parent: avnm + location: location + tags: tags + properties: { + addressPrefixes: platformCIDRs + parentPoolName: regionIpamPool.name + displayName: 'Platform IPAM pool - ${regionDisplayName}' + description: 'IPAM pool for Platform Landing Zones in the ${regionDisplayName} region' + } +} + +@description('Resource: Application IPAM Pool for the region.') +resource applicationIpamPool 'Microsoft.Network/networkManagers/ipamPools@2024-05-01' = if (deploy) { + name: 'applicationIpamPool-${replace(location, ' ', '-')}' + parent: avnm + location: location + tags: tags + properties: { + addressPrefixes: applicationCIDRs + parentPoolName: regionIpamPool.name + displayName: 'Application IPAM pool - ${regionDisplayName}' + description: 'IPAM pool for Application Landing Zones in the ${regionDisplayName} region' + } +} + +// Outputs +@description('Region CIDR.') +output regionCIDR string = regionCidr + +// Platform Landing Zone CIDRs +@description('Platform Landing Zone CIDRs.') +output platformCIDRs array = platformCIDRs + +@description('Count of Platform Landing Zone CIDRs.') +output platformCIDRsCount int = length(platformCIDRs) + +// Application Landing Zone CIDRs +@description('Application Landing Zone CIDRs.') +output applicationCIDRs array = applicationCIDRs + +@description('Count of Application Landing Zone CIDRs.') +output applicationCIDRsCount int = length(applicationCIDRs) +