From 5d8c00aa7585fc13257557243fa53b1a804c2786 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Mon, 8 Apr 2024 09:30:21 +0200 Subject: [PATCH 01/66] feat: Added subscription cleanup option & resourceId list input to removal function (#1607) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description - Added ResourceId list input to removal function - Added option to fetch any resource & resourceGroup with `dep-` in its name to be removed using the default removal logic
Initial execution ``` PS C:\dev\bicep-registry-modules> Initialize-DeploymentRemoval -RemoveAllDepResources VERBOSE: Total number of deployment target resources after fetching deployments [79] VERBOSE: Total number of deployment target resources after pre-filtering (duplicates) & ordering items [79] VERBOSE: Total number of deployment target resources after formatting items [79] VERBOSE: Total number of deployments after filtering all dependency resources [79] VERBOSE: Total number of deployments after final ordering of resources [79] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressRouteGateway-nergmin-rg/providers/Microsoft.Network/virtualWans/dep--vwan-nergmin] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/dnsResolvers/dep--ndr-ndfrswaf/outboundEndpoints/pdnsout] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--keyvault.vaults-kvvwaf-rg/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net/virtualNetworkLinks/dep--vnet-kvvwaf-vnetlink] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrwaf-rg/providers/Microsoft.Network/dnsResolvers/ndrwaf001/inboundEndpoints/ndrwaf-az-pdnsin-x-001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--sql.servers-sqlspe-rg/providers/Microsoft.Sql/servers/-sqlspe/databases/master] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrwaf-rg/providers/Microsoft.Network/dnsResolvers/ndrwaf001/outboundEndpoints/ndrwaf-az-pdnsout-x-001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg/providers/Microsoft.Storage/storageAccounts/depdiasanvnwaf01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg/providers/Microsoft.Network/virtualNetworks/nvnwaf001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg/providers/Microsoft.Network/routeTables/dep--rt-nvnwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg/providers/Microsoft.Network/networkSecurityGroups/dep--nsg-nvnwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg/providers/Microsoft.Network/networkSecurityGroups/dep--nsg-bastion-nvnwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-nvnwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnpeer-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-nvnpeer] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnpeer-rg/providers/Microsoft.Network/virtualNetworks/nvnpeer001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnpeer-rg/providers/Microsoft.Network/networkSecurityGroups/dep--nsg-bastion-nvnpeer] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg/providers/Microsoft.Storage/storageAccounts/depdiasanvnmax01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg/providers/Microsoft.Network/virtualNetworks/nvnmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg/providers/Microsoft.Network/routeTables/dep--rt-nvnmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg/providers/Microsoft.Network/networkSecurityGroups/dep--nsg-nvnmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwadv-rg/providers/Microsoft.Automation/automationAccounts/dep--auto-oiwadv] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwadv-rg/providers/Microsoft.EventHub/namespaces/dep--ehn-oiwadv] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwadv-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-oiwadv] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwadv-rg/providers/Microsoft.Storage/storageAccounts/depdiasaoiwadv01] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwmax-rg-managed/providers/Microsoft.Storage/storageAccounts/saavmxdwmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwmax-rg-managed/providers/Microsoft.Databricks/accessConnectors/unity-catalog-access-connector] VERBOSE: - Remove [/subscriptions//resourceGroups/IT_dep--compute.images-cimin-rg_dep--_0cde4fa3-334a-4ea8-be55-f9ca6584e6e1/providers/Microsoft.Storage/storageAccounts/q3qok4o2xgwxwvuvs86hucv0] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--synapse.workspaces-swenua-rg/providers/Microsoft.Synapse/workspaces/swenua001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--synapse.workspaces-swenua-rg/providers/Microsoft.Storage/storageAccounts/depsaswenua01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg/providers/Microsoft.Network/networkSecurityGroups/dep--nsg-bastion-nvnmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--servicebus.namespaces-sbnmin-rg/providers/Microsoft.ServiceBus/namespaces/sbnmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--search.searchservices-sssmax-rg/providers/Microsoft.Search/searchServices/sssmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--search.searchservices-sssmax-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-sssmax01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwwaf-rg/providers/Microsoft.Storage/storageAccounts/depsaoiwwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwwaf-rg/providers/Microsoft.Storage/storageAccounts/depdiasaoiwwaf01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwwaf-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-oiwwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwwaf-rg/providers/Microsoft.Automation/automationAccounts/dep--auto-oiwwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwadv-rg/providers/Microsoft.Storage/storageAccounts/depsaoiwadv] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--search.searchservices-sssmax-rg/providers/Microsoft.Storage/storageAccounts/depdiasasssmax03] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-nvnmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.publicipprefixes-npipmin-rg/providers/Microsoft.Network/publicIPPrefixes/npipmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed/providers/Microsoft.Databricks/accessConnectors/unity-catalog-access-connector] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--keyvault.vaults-kvvwaf-rg/providers/Microsoft.Storage/storageAccounts/depdiasakvvwaf03] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--keyvault.vaults-kvvwaf-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-kvvwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--keyvault.vaults-kvvwaf-rg/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--keyvault.vaults-kvvwaf-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-kvvwaf01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.dataCollectionEndpoints-idcemin-rg/providers/Microsoft.Insights/dataCollectionEndpoints/idcemin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.components-icmax-rg/providers/Microsoft.Storage/storageAccounts/depsaicmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.components-icmax-rg/providers/Microsoft.Storage/storageAccounts/depdiasaicmax01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.components-icmax-rg/providers/Microsoft.Insights/components/icmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.components-icmax-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-icmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.components-icmax-rg/providers/microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - icmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--eventhub.namespaces-ehnenc-rg/providers/Microsoft.EventHub/namespaces/ehnenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--digitaltwins.digitaltwinsinstances-dtdiwaf-rg/providers/Microsoft.Storage/storageAccounts/depdiasadtdiwaf03] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--digitaltwins.digitaltwinsinstances-dtdiwaf-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-dtdiwaf01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--digitaltwins.digitaltwinsinstances-dtdiwaf-rg/providers/Microsoft.DigitalTwins/digitalTwinsInstances/dtdiwaf001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--logic.workflows-lwwaf-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-lwwaf01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.publicipprefixes-npipwaf-rg/providers/Microsoft.Network/publicIPPrefixes/npipwaf001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--logic.workflows-lwwaf-rg/providers/Microsoft.Logic/workflows/lwwaf001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.applicationGatewayWebApplicationFirewallPolicies-nagwafpmax-rg/providers/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/nagwafpmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.privateendpoints-npemin-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-npemin] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.networkmanagers-nnmmin-rg/providers/Microsoft.Network/networkManagers/nnmmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.firewallpolicies-nfpmin-rg/providers/Microsoft.Network/firewallPolicies/nfpmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressroutecircuits-nercmin-rg/providers/Microsoft.Network/expressRouteCircuits/nercmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressroutecircuits-nercmax-rg/providers/Microsoft.Storage/storageAccounts/depdiasanercmax01] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressroutecircuits-nercmax-rg/providers/Microsoft.Network/expressRouteCircuits/nercmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressroutecircuits-nercmax-rg/providers/Microsoft.EventHub/namespaces/dep--evhns-nercmax] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrwaf-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-ndrwaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrwaf-rg/providers/Microsoft.Network/dnsResolvers/ndrwaf001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrmin-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-ndrmin] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrmin-rg/providers/Microsoft.Network/dnsResolvers/ndrmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-ndfrswaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/dnsResolvers/dep--ndr-ndfrswaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/dnsForwardingRulesets/ndfrswaf001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--logic.workflows-lwwaf-rg/providers/Microsoft.Storage/storageAccounts/depdiasalwwaf01] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed/providers/Microsoft.Storage/storageAccounts/saavmxdwwaf001] VERBOSE: ---------------------------------- VERBOSE: [-] Removing resource [dep--vwan-nergmin] of type [Microsoft.Network/virtualWans] VERBOSE: [-] Removing resource [pdnsout] of type [Microsoft.Network/dnsResolvers/outboundEndpoints] WARNING: [!] Removal moved back for retry. Reason: [BadRequest : Existing outbound endpoint has DNS forwarding ruleset links that have to be deleted first before outbound endpoint can be deleted. outboundEndpointResourceId=/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/dnsResolvers/dep--ndr-ndfrswaf/outboundEndpoints/pdnsout, numberOfDnsForwardingRulesetLinks=1 CorrelationId: 5bca8494-6de9-4c01-be0d-5988811d0d9b] VERBOSE: [-] Removing resource [dep--vnet-kvvwaf-vnetlink] of type [Microsoft.Network/privateDnsZones/virtualNetworkLinks] DELETE /subscriptions//r. [Starting - 0,00 % completed. ] DELETE /subscriptions//r. [Starting - 0,00 % completed. ] VERBOSE: [-] Removing resource [ndrwaf-az-pdnsin-x-001] of type [Microsoft.Network/dnsResolvers/inboundEndpoints] VERBOSE: [-] Removing resource [master] of type [Microsoft.Sql/servers/databases] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'dep--sql.servers-sqlspe-rg' could not be found. CorrelationId: 856b562e-9cc7-416a-9a00-797667b04ecb] VERBOSE: [-] Removing resource [ndrwaf-az-pdnsout-x-001] of type [Microsoft.Network/dnsResolvers/outboundEndpoints] VERBOSE: [-] Removing resource [cicgenc001] of type [Microsoft.ContainerInstance/containerGroups] WARNING: [!] Removal moved back for retry. Reason: [InternalServerError : Encountered an internal server error. The tracking activity id is '31388eb2-15b6-40fc-91d8-c4e9e92cba59', correlation id is 'f97aa916-0644-41b1-976b-8cc1f800bc3a'. CorrelationId: f97aa916-0644-41b1-976b-8cc1f800bc3a] VERBOSE: [-] Removing resource [depdiasanvnwaf01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [nvnwaf001] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [dep--rt-nvnwaf] of type [Microsoft.Network/routeTables] VERBOSE: [-] Removing resource [dep--nsg-nvnwaf] of type [Microsoft.Network/networkSecurityGroups] VERBOSE: [-] Removing resource [dep--nsg-bastion-nvnwaf] of type [Microsoft.Network/networkSecurityGroups] VERBOSE: [-] Removing resource [dep--evhns-nvnwaf] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [dep--vnet-nvnpeer] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [nvnpeer001] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [dep--nsg-bastion-nvnpeer] of type [Microsoft.Network/networkSecurityGroups] VERBOSE: [-] Removing resource [depdiasanvnmax01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [nvnmax001] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [dep--rt-nvnmax] of type [Microsoft.Network/routeTables] VERBOSE: [-] Removing resource [dep--nsg-nvnmax] of type [Microsoft.Network/networkSecurityGroups] VERBOSE: [-] Removing resource [dep--auto-oiwadv] of type [Microsoft.Automation/automationAccounts] VERBOSE: [-] Removing resource [dep--ehn-oiwadv] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [dep--evhns-oiwadv] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [depdiasaoiwadv01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [saavmxdwmax001] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [unity-catalog-access-connector] of type [Microsoft.Databricks/accessConnectors] VERBOSE: [-] Removing resource [q3qok4o2xgwxwvuvs86hucv0] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [avmrhbhbmin001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: ffb58929-b5db-48bb-aaa0-45285bf31114] VERBOSE: [-] Removing resource [avmrhbhbmax001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 9bc0f1d4-e469-4569-83da-9bd95df04feb] VERBOSE: [-] Removing resource [swenua001] of type [Microsoft.Synapse/workspaces] VERBOSE: [-] Removing resource [depsaswenua01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [dep--nsg-bastion-nvnmax] of type [Microsoft.Network/networkSecurityGroups] VERBOSE: [-] Removing resource [sbnmin001] of type [Microsoft.ServiceBus/namespaces] VERBOSE: [-] Removing resource [sssmax001] of type [Microsoft.Search/searchServices] VERBOSE: [-] Removing resource [dep--evhns-sssmax01] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [depsaoiwwaf] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [depdiasaoiwwaf01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [dep--evhns-oiwwaf] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [dep--auto-oiwwaf] of type [Microsoft.Automation/automationAccounts] VERBOSE: [-] Removing resource [depsaoiwadv] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [depdiasasssmax03] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [dep--evhns-nvnmax] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [npipmin001] of type [Microsoft.Network/publicIPPrefixes] VERBOSE: [-] Removing resource [unity-catalog-access-connector] of type [Microsoft.Databricks/accessConnectors] VERBOSE: [-] Removing resource [depdiasakvvwaf03] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [dep--vnet-kvvwaf] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [privatelink.vaultcore.azure.net] of type [Microsoft.Network/privateDnsZones] VERBOSE: [-] Removing resource [dep--evhns-kvvwaf01] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [idcemin001] of type [Microsoft.Insights/dataCollectionEndpoints] VERBOSE: [-] Removing resource [depsaicmax] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [depdiasaicmax01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [icmax001] of type [Microsoft.Insights/components] VERBOSE: [-] Removing resource [dep--evhns-icmax] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [Failure Anomalies - icmax001] of type [microsoft.alertsmanagement/smartDetectorAlertRules] VERBOSE: [-] Removing resource [dep--vnet-ehnenc] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [ehnenc001] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [depdiasadtdiwaf03] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [dep--evhns-dtdiwaf01] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [dtdiwaf001] of type [Microsoft.DigitalTwins/digitalTwinsInstances] VERBOSE: [-] Removing resource [dep--evhns-lwwaf01] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [npipwaf001] of type [Microsoft.Network/publicIPPrefixes] VERBOSE: [-] Removing resource [lwwaf001] of type [Microsoft.Logic/workflows] VERBOSE: [-] Removing resource [nagwafpmax001] of type [Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies] VERBOSE: [-] Removing resource [dep--vnet-npemin] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [nnmmin001] of type [Microsoft.Network/networkManagers] VERBOSE: [-] Removing resource [nfpmin001] of type [Microsoft.Network/firewallPolicies] VERBOSE: [-] Removing resource [nercmin001] of type [Microsoft.Network/expressRouteCircuits] VERBOSE: [-] Removing resource [depdiasanercmax01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [nercmax001] of type [Microsoft.Network/expressRouteCircuits] VERBOSE: [-] Removing resource [dep--evhns-nercmax] of type [Microsoft.EventHub/namespaces] VERBOSE: [-] Removing resource [dep--vnet-ndrwaf] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [ndrwaf001] of type [Microsoft.Network/dnsResolvers] VERBOSE: [-] Removing resource [dep--vnet-ndrmin] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [ndrmin001] of type [Microsoft.Network/dnsResolvers] VERBOSE: [-] Removing resource [dep--vnet-ndfrswaf] of type [Microsoft.Network/virtualNetworks] WARNING: [!] Removal moved back for retry. Reason: [InUseSubnetCannotBeDeleted : Subnet subnet-1 is in use by /subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-ndfrswaf/subnets/subnet-1/serviceAssociationLinks/dnsResolverLink and cannot be deleted. In order to delete the subnet, delete all the resources within the subnet. See aka.ms/deletesubnet. CorrelationId: 636498cc-a349-4dbd-8c67-c9d2e020db3e] VERBOSE: [-] Removing resource [dep--ndr-ndfrswaf] of type [Microsoft.Network/dnsResolvers] WARNING: [!] Removal moved back for retry. Reason: [CannotDeleteResource : Cannot delete resource while nested resources exist. Some existing nested resource IDs include: 'Microsoft.Network/dnsResolvers/dep--ndr-ndfrswaf/outboundEndpoints/pdnsout'. Please delete all nested resources before deleting this resource. CorrelationId: 5a56e4c8-d626-46bb-bd81-783138352cae] VERBOSE: [-] Removing resource [ndfrswaf001] of type [Microsoft.Network/dnsForwardingRulesets] VERBOSE: [-] Removing resource [depdiasalwwaf01] of type [Microsoft.Storage/storageAccounts] VERBOSE: [-] Removing resource [saavmxdwwaf001] of type [Microsoft.Storage/storageAccounts] VERBOSE: ---------------------------------- VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/dnsResolvers/dep--ndr-ndfrswaf/outboundEndpoints/pdnsout] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--sql.servers-sqlspe-rg/providers/Microsoft.Sql/servers/-sqlspe/databases/master] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/virtualNetworks/dep--vnet-ndfrswaf] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg/providers/Microsoft.Network/dnsResolvers/dep--ndr-ndfrswaf] VERBOSE: ---------------------------------- VERBOSE: [-] Removing resource [pdnsout] of type [Microsoft.Network/dnsResolvers/outboundEndpoints] VERBOSE: [-] Removing resource [master] of type [Microsoft.Sql/servers/databases] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'dep--sql.servers-sqlspe-rg' could not be found. CorrelationId: 15c85c04-aaa5-41a8-a012-5db2d08b2f88] VERBOSE: [-] Removing resource [cicgenc001] of type [Microsoft.ContainerInstance/containerGroups] WARNING: [!] Removal moved back for retry. Reason: [InternalServerError : Encountered an internal server error. The tracking activity id is '3220689c-83cf-45f8-a4a1-5c12150cf1bb', correlation id is '577dd566-cd51-487e-9c98-cc0e4486ce02'. CorrelationId: 577dd566-cd51-487e-9c98-cc0e4486ce02] VERBOSE: [-] Removing resource [avmrhbhbmin001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 81d5f560-b89c-4159-ad4c-b3aba2dd5f12] VERBOSE: [-] Removing resource [avmrhbhbmax001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 1dd1305e-d7e2-4166-8193-78a6e053759c] VERBOSE: [-] Removing resource [dep--vnet-ndfrswaf] of type [Microsoft.Network/virtualNetworks] VERBOSE: [-] Removing resource [dep--ndr-ndfrswaf] of type [Microsoft.Network/dnsResolvers] VERBOSE: ---------------------------------- VERBOSE: - Remove [/subscriptions//resourceGroups/dep--sql.servers-sqlspe-rg/providers/Microsoft.Sql/servers/-sqlspe/databases/master] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001] VERBOSE: ---------------------------------- VERBOSE: [-] Removing resource [master] of type [Microsoft.Sql/servers/databases] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'dep--sql.servers-sqlspe-rg' could not be found. CorrelationId: 632be08b-bc75-461e-988c-5269717cb199] VERBOSE: [-] Removing resource [cicgenc001] of type [Microsoft.ContainerInstance/containerGroups] WARNING: [!] Removal moved back for retry. Reason: [InternalServerError : Encountered an internal server error. The tracking activity id is 'be6e61ad-ec39-4c10-86c8-ed36817db00a', correlation id is '696735a0-485f-44e6-9ecf-92915f9db5b4'. CorrelationId: 696735a0-485f-44e6-9ecf-92915f9db5b4] VERBOSE: [-] Removing resource [avmrhbhbmin001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 39e58329-ed01-43c2-868c-0dfc710198db] VERBOSE: [-] Removing resource [avmrhbhbmax001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 5f7a64b8-2d6e-4ea8-a8b9-03face113b0b] VERBOSE: ---------------------------------- Exception: C:\dev\bicep-registry-modules\avm\utilities\pipelines\e2eValidation\resourceRemoval\helper\Remove-ResourceList.ps1:124:9 Line | 124 | throw ('The removal failed for resources [{0}]' -f ((Split-Pa … | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | The removal failed for resources [master, cicgenc001, avmrhbhbmin001, avmrhbhbmax001] ```
Execution after adding RG support ``` PS C:\dev\bicep-registry-modules> Initialize-DeploymentRemoval -RemoveAllDepResources VERBOSE: Total number of deployment target resources after fetching deployments [36] VERBOSE: Total number of deployment target resources after pre-filtering (duplicates) & ordering items [36] VERBOSE: Total number of deployment target resources after formatting items [36] VERBOSE: Total number of deployments after filtering all dependency resources [36] VERBOSE: Total number of deployments after final ordering of resources [36] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.publicipprefixes-npipmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.publicipprefixes-npipwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnpeer-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.virtualnetworks-nvnwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwadv-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--search.searchservices-sssmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.privateendpoints-npemin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--servicebus.namespaces-sbnmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--synapse.workspaces-swenua-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/IT_dep--compute.images-cimin-rg_dep--_0cde4fa3-334a-4ea8-be55-f9ca6584e6e1] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--operationalinsights.workspaces-oiwwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.networkmanagers-nnmmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressRouteGateway-nergmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwmax-rg-managed] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressroutecircuits-nercmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.expressroutecircuits-nercmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsResolvers-ndrmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.dnsForwardingRuleset-ndfrswaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.applicationGatewayWebApplicationFirewallPolicies-nagwafpmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--logic.workflows-lwwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--keyvault.vaults-kvvwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.dataCollectionEndpoints-idcemin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--insights.components-icmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--eventhub.namespaces-ehnenc-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--digitaltwins.digitaltwinsinstances-dtdiwaf-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--network.firewallpolicies-nfpmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--sql.servers-sqlspe-rg/providers/Microsoft.Sql/servers/-sqlspe/databases/master] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001] VERBOSE: ---------------------------------- VERBOSE: [-] Removing resource [dep--network.publicipprefixes-npipmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.publicipprefixes-npipwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.virtualnetworks-nvnmax-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.virtualnetworks-nvnpeer-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.virtualnetworks-nvnwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--operationalinsights.workspaces-oiwadv-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--search.searchservices-sssmax-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.privateendpoints-npemin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--servicebus.namespaces-sbnmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--synapse.workspaces-swenua-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep-avmr-healthbot.healthbots-hbhbmax-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionBlocked","message":"Deletion of resource group 'dep-avmr-healthbot.healthbots-hbhbmax-rg' failed as resources with identifiers 'Microsoft.HealthBot/healthBots/avmrhbhbmax001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '270ed8ad-68a0-476a-942c-6d22377c36a9'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001","message":"{\"error\":{\"code\":\"400\",\"message\":\"Resource not in terminal state. Wait until resource is in terminal provisioningState and try again\"}}"}]}} CorrelationId: b90bb002-14f7-4032-832b-69ae37e3b30a] VERBOSE: [-] Removing resource [dep-avmr-healthbot.healthbots-hbhbmin-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionBlocked","message":"Deletion of resource group 'dep-avmr-healthbot.healthbots-hbhbmin-rg' failed as resources with identifiers 'Microsoft.HealthBot/healthBots/avmrhbhbmin001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '701812e8-5059-4a92-97d6-c0d35fc92d44'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001","message":"{\"error\":{\"code\":\"400\",\"message\":\"Resource not in terminal state. Wait until resource is in terminal provisioningState and try again\"}}"}]}} CorrelationId: 3ca89140-4603-49e7-93e3-0c959fb97014] VERBOSE: [-] Removing resource [IT_dep--compute.images-cimin-rg_dep--_0cde4fa3-334a-4ea8-be55-f9ca6584e6e1] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--operationalinsights.workspaces-oiwwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.networkmanagers-nnmmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.expressRouteGateway-nergmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [rg-dep-avmx-databricks.workspaces-dwmax-rg-managed] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.expressroutecircuits-nercmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.expressroutecircuits-nercmax-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.dnsResolvers-ndrwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.dnsResolvers-ndrmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.dnsForwardingRuleset-ndfrswaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--network.applicationGatewayWebApplicationFirewallPolicies-nagwafpmax-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--logic.workflows-lwwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--keyvault.vaults-kvvwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--insights.dataCollectionEndpoints-idcemin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--insights.components-icmax-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--eventhub.namespaces-ehnenc-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--digitaltwins.digitaltwinsinstances-dtdiwaf-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [dep--containerinstance.containergroups-cicgenc-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionTimeout","message":"Deletion of resource group 'dep--containerinstance.containergroups-cicgenc-rg' did not finish within the allowed time as resources with identifiers 'Microsoft.ContainerInstance/containerGroups/cicgenc001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '3ab01252-3168-4ef2-8491-5317dae1db41'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001","message":"{\"error\":{\"code\":\"InternalServerError\",\"message\":\"Encountered an internal server error. The tracking activity id is 'c0cd7e57-12ec-4d56-a001-c03780eef578', correlation id is '3ab01252-3168-4ef2-8491-5317dae1db41'.\"}}"}]}} CorrelationId: 873c04ae-e730-4e41-adb1-e85f7b8f9fef] VERBOSE: [-] Removing resource [dep--network.firewallpolicies-nfpmin-rg] of type [Microsoft.Resources/resourceGroups] VERBOSE: [-] Removing resource [rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed' could not be found. CorrelationId: 6c61fe5d-7233-4ad6-b33a-c4ef906bd270] VERBOSE: [-] Removing resource [master] of type [Microsoft.Sql/servers/databases] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'dep--sql.servers-sqlspe-rg' could not be found. CorrelationId: 2c130ff7-ca6b-462d-a722-fbf18fbd86e8] VERBOSE: [-] Removing resource [cicgenc001] of type [Microsoft.ContainerInstance/containerGroups] WARNING: [!] Removal moved back for retry. Reason: [InternalServerError : Encountered an internal server error. The tracking activity id is '609139de-e745-49a7-922c-d40caf735f18', correlation id is 'b913c064-03dd-4e7a-ab30-4e39155d12ef'. CorrelationId: b913c064-03dd-4e7a-ab30-4e39155d12ef] VERBOSE: [-] Removing resource [avmrhbhbmax001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: c00fbfc3-307d-4129-a24b-0957c01d453a] VERBOSE: [-] Removing resource [avmrhbhbmin001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: e73374cb-84e0-40af-adbd-f09607d63879] VERBOSE: ---------------------------------- VERBOSE: Retry removal of remaining [8] resources. Waiting [15] seconds. Round [1|3] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--sql.servers-sqlspe-rg/providers/Microsoft.Sql/servers/-sqlspe/databases/master] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001] VERBOSE: ---------------------------------- VERBOSE: [-] Removing resource [dep-avmr-healthbot.healthbots-hbhbmax-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionBlocked","message":"Deletion of resource group 'dep-avmr-healthbot.healthbots-hbhbmax-rg' failed as resources with identifiers 'Microsoft.HealthBot/healthBots/avmrhbhbmax001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '4b5a669c-71fa-4872-a74e-f7dc9e42fdcd'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001","message":"{\"error\":{\"code\":\"400\",\"message\":\"Resource not in terminal state. Wait until resource is in terminal provisioningState and try again\"}}"}]}} CorrelationId: f027db06-7884-44cd-bbd5-503b9f853401] VERBOSE: [-] Removing resource [dep-avmr-healthbot.healthbots-hbhbmin-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionBlocked","message":"Deletion of resource group 'dep-avmr-healthbot.healthbots-hbhbmin-rg' failed as resources with identifiers 'Microsoft.HealthBot/healthBots/avmrhbhbmin001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '895a795f-d6fb-4df0-90ad-05d5e5767bc8'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001","message":"{\"error\":{\"code\":\"400\",\"message\":\"Resource not in terminal state. Wait until resource is in terminal provisioningState and try again\"}}"}]}} CorrelationId: 290f9cb4-27d9-49e8-8e5c-89aeb1b24548] VERBOSE: [-] Removing resource [dep--containerinstance.containergroups-cicgenc-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionTimeout","message":"Deletion of resource group 'dep--containerinstance.containergroups-cicgenc-rg' did not finish within the allowed time as resources with identifiers 'Microsoft.ContainerInstance/containerGroups/cicgenc001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '9068dc6f-57ad-4b54-a0b9-0a97223e8eae'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001","message":"{\"error\":{\"code\":\"InternalServerError\",\"message\":\"Encountered an internal server error. The tracking activity id is '5119291c-288d-48ab-b1e1-dfa90c29cad6', correlation id is '9068dc6f-57ad-4b54-a0b9-0a97223e8eae'.\"}}"}]}} CorrelationId: 68196e76-7bdd-4e47-ab29-ad51b6eddcd4] VERBOSE: [-] Removing resource [rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed' could not be found. CorrelationId: 1b17ec09-6039-4785-9112-e43acb7cda1f] VERBOSE: [-] Removing resource [master] of type [Microsoft.Sql/servers/databases] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'dep--sql.servers-sqlspe-rg' could not be found. CorrelationId: 4e03c4f3-4605-4ad4-b672-f37b231fecbb] VERBOSE: [-] Removing resource [cicgenc001] of type [Microsoft.ContainerInstance/containerGroups] WARNING: [!] Removal moved back for retry. Reason: [InternalServerError : Encountered an internal server error. The tracking activity id is '92c169ae-e964-4bce-a31d-f13307e729ea', correlation id is '0eec5b46-fc55-4e88-9409-95f1c9933cd4'. CorrelationId: 0eec5b46-fc55-4e88-9409-95f1c9933cd4] VERBOSE: [-] Removing resource [avmrhbhbmax001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: edb388a1-6e3e-48fd-803f-4c173ffe8184] VERBOSE: [-] Removing resource [avmrhbhbmin001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 3f721795-2e7f-4cb9-bf40-e41e29589f23] VERBOSE: ---------------------------------- VERBOSE: Retry removal of remaining [8] resources. Waiting [15] seconds. Round [2|3] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg] VERBOSE: - Remove [/subscriptions//resourceGroups/rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--sql.servers-sqlspe-rg/providers/Microsoft.Sql/servers/-sqlspe/databases/master] VERBOSE: - Remove [/subscriptions//resourceGroups/dep--containerinstance.containergroups-cicgenc-rg/providers/Microsoft.ContainerInstance/containerGroups/cicgenc001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001] VERBOSE: - Remove [/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001] VERBOSE: ---------------------------------- VERBOSE: [-] Removing resource [dep-avmr-healthbot.healthbots-hbhbmax-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionBlocked","message":"Deletion of resource group 'dep-avmr-healthbot.healthbots-hbhbmax-rg' failed as resources with identifiers 'Microsoft.HealthBot/healthBots/avmrhbhbmax001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '672f3094-43a1-4eb6-859b-f9e2fe7d086f'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmax-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmax001","message":"{\"error\":{\"code\":\"400\",\"message\":\"Resource not in terminal state. Wait until resource is in terminal provisioningState and try again\"}}"}]}} CorrelationId: 3ad140d1-9d60-47fc-83ae-4cbdc0ce33e3] VERBOSE: [-] Removing resource [dep-avmr-healthbot.healthbots-hbhbmin-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [{"error":{"code":"ResourceGroupDeletionBlocked","message":"Deletion of resource group 'dep-avmr-healthbot.healthbots-hbhbmin-rg' failed as resources with identifiers 'Microsoft.HealthBot/healthBots/avmrhbhbmin001' could not be deleted. The provisioning state of the resource group will be rolled back. The tracking Id is '721645e9-ac6b-450a-a08a-e05e57dee3a3'. Please check audit logs for more details.","details":[{"target":"/subscriptions//resourceGroups/dep-avmr-healthbot.healthbots-hbhbmin-rg/providers/Microsoft.HealthBot/healthBots/avmrhbhbmin001","message":"{\"error\":{\"code\":\"400\",\"message\":\"Resource not in terminal state. Wait until resource is in terminal provisioningState and try again\"}}"}]}} CorrelationId: de39ed72-ca70-43be-950f-0177cfdfc54a] VERBOSE: [-] Removing resource [dep--containerinstance.containergroups-cicgenc-rg] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [No such host is known. (management.azure.com:443)] VERBOSE: [-] Removing resource [rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed] of type [Microsoft.Resources/resourceGroups] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed' could not be found. CorrelationId: e8f0a716-c316-4179-9f18-5f364c6df5bf] VERBOSE: [-] Removing resource [master] of type [Microsoft.Sql/servers/databases] WARNING: [!] Removal moved back for retry. Reason: [ResourceGroupNotFound : Resource group 'dep--sql.servers-sqlspe-rg' could not be found. CorrelationId: 62edcdde-c886-4638-9f33-1c8c96f3dc12] VERBOSE: [-] Removing resource [cicgenc001] of type [Microsoft.ContainerInstance/containerGroups] WARNING: [!] Removal moved back for retry. Reason: [InternalServerError : Encountered an internal server error. The tracking activity id is '819de35f-9f5b-4782-801b-833fb84309e2', correlation id is '37ca6aaa-87f3-48ff-bcc9-6acf23fa80bc'. CorrelationId: 37ca6aaa-87f3-48ff-bcc9-6acf23fa80bc] VERBOSE: [-] Removing resource [avmrhbhbmax001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: fb86ed27-6d42-4393-b04d-08e43b07904d] VERBOSE: [-] Removing resource [avmrhbhbmin001] of type [Microsoft.HealthBot/healthBots] WARNING: [!] Removal moved back for retry. Reason: [400 : Resource not in terminal state. Wait until resource is in terminal provisioningState and try again CorrelationId: 7cbb2e66-2269-4372-9ce6-d3a2163b76d2] VERBOSE: ---------------------------------- VERBOSE: Retry removal of remaining [8] resources. Waiting [15] seconds. Round [3|3] Exception: C:\dev\bicep-registry-modules\avm\utilities\pipelines\e2eValidation\resourceRemoval\helper\Remove-ResourceList.ps1:124:9 Line | 124 | throw ('The removal failed for resources [{0}]' -f ((Split-Pa … | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | The removal failed for resources [dep-avmr-healthbot.healthbots-hbhbmax-rg, dep-avmr-healthbot.healthbots-hbhbmin-rg, dep--containerinstance.containergroups-cicgenc-rg, | rg-dep-avmx-databricks.workspaces-dwwaf-rg-managed, master, cicgenc001, avmrhbhbmax001, avmrhbhbmin001] ```
## Pipeline Reference | Pipeline | | -------- | | [![avm.res.analysis-services.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml/badge.svg?branch=users%2Falsehr%2FresourceRemoval&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml) | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- .../Initialize-DeploymentRemoval.ps1 | 60 +++++++++++++--- .../helper/Remove-Deployment.ps1 | 71 +++++++++++-------- 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 index 5939f2a474..2f8a7b1a01 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/Initialize-DeploymentRemoval.ps1 @@ -6,10 +6,13 @@ Remove deployed resources based on their deploymentName(s) Remove deployed resources based on their deploymentName(s) .PARAMETER DeploymentName(s) -Mandatory. The name(s) of the deployment(s) +Optional. The name(s) of the deployment(s). Combined with resources provide via the resource Id(s). + +.PARAMETER ResourceId(s) +Optional. The resource Id(s) of the resources to remove. Combined with resources found via the deployment name(s). .PARAMETER TemplateFilePath -Mandatory. The path to the template used for the deployment. Used to determine the level/scope (e.g. subscription) +Optional. The path to the template used for the deployment(s). Used to determine the level/scope (e.g. subscription). Required if deploymentName(s) are provided. .PARAMETER ResourceGroupName Optional. The name of the resource group the deployment was happening in. Relevant for resource-group level deployments. @@ -17,6 +20,9 @@ Optional. The name of the resource group the deployment was happening in. Releva .PARAMETER ManagementGroupId Optional. The ID of the management group to fetch deployments from. Relevant for management-group level deployments. +.PARAMETER PurgeTestResources +Optional. Specify to fetch and remove all resources in the current context that match the 'dep-' pattern + .EXAMPLE Initialize-DeploymentRemoval -DeploymentName 'n-vw-t1-20211204T1812029146Z' -TemplateFilePath "$home/ResourceModules/modules/network/virtual-wan/main.bicep" -resourceGroupName 'test-virtualWan-rg' @@ -26,21 +32,28 @@ function Initialize-DeploymentRemoval { [CmdletBinding()] param ( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $false)] [Alias('DeploymentName')] - [string[]] $DeploymentNames, + [string[]] $DeploymentNames = @(), - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $false)] + [Alias('ResourceId')] + [string[]] $ResourceIds = @(), + + [Parameter(Mandatory = $false)] [string] $TemplateFilePath, [Parameter(Mandatory = $false)] [string] $ResourceGroupName, [Parameter(Mandatory = $false)] - [string] $subscriptionId, + [string] $SubscriptionId, + + [Parameter(Mandatory = $false)] + [string] $ManagementGroupId, [Parameter(Mandatory = $false)] - [string] $ManagementGroupId + [switch] $PurgeTestResources ) begin { @@ -82,7 +95,18 @@ function Initialize-DeploymentRemoval { 'Microsoft.Resources/resourceGroups' ) - Write-Verbose ('Handling resource removal with deployment names [{0}]' -f ($deploymentNames -join ', ')) -Verbose + if ($DeploymentNames.Count -gt 0) { + Write-Verbose 'Handling resource removal with deployment names' -Verbose + foreach ($DeploymentName in $DeploymentNames) { + Write-Verbose "- $DeploymentName" -Verbose + } + } + if ($ResourceIds.Count -gt 0) { + Write-Verbose 'Handling resource removal with resource Ids' -Verbose + foreach ($ResourceId in $ResourceIds) { + Write-Verbose "- $ResourceId" -Verbose + } + } ### CODE LOCATION: Add custom removal sequence here ## Add custom module-specific removal sequence following the example below @@ -98,14 +122,28 @@ function Initialize-DeploymentRemoval { # } # } + if ($PurgeTestResources) { + # Resources + $filteredResourceIds = (Get-AzResource).ResourceId | Where-Object { $_ -like '*dep-*' } + $ResourceIds += ($filteredResourceIds | Sort-Object -Unique) + + # Resource groups + $filteredResourceGroupIds = (Get-AzResourceGroup).ResourceId | Where-Object { $_ -like '*dep-*' } + $ResourceIds += ($filteredResourceGroupIds | Sort-Object -Unique) + } + # Invoke removal $inputObject = @{ DeploymentNames = $DeploymentNames - TemplateFilePath = $templateFilePath + ResourceIds = $ResourceIds + TemplateFilePath = $TemplateFilePath RemovalSequence = $removalSequence } - if (-not [String]::IsNullOrEmpty($resourceGroupName)) { - $inputObject['resourceGroupName'] = $resourceGroupName + if (-not [String]::IsNullOrEmpty($TemplateFilePath)) { + $inputObject['TemplateFilePath'] = $TemplateFilePath + } + if (-not [String]::IsNullOrEmpty($ResourceGroupName)) { + $inputObject['ResourceGroupName'] = $ResourceGroupName } if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { $inputObject['ManagementGroupId'] = $ManagementGroupId diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 index ee61d87d7a..e15a259a87 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Remove-Deployment.ps1 @@ -15,11 +15,14 @@ Optional. The resource group of the resource to remove .PARAMETER ManagementGroupId Optional. The ID of the management group to fetch deployments from. Relevant for management-group level deployments. -.PARAMETER DeploymentNames -Optional. The deployment names to use for the removal +.PARAMETER DeploymentName(s) +Optional. The name(s) of the deployment(s). Combined with resources provide via the resource Id(s). + +.PARAMETER ResourceId(s) +Optional. The resource Id(s) of the resources to remove. Combined with resources found via the deployment name(s). .PARAMETER TemplateFilePath -Mandatory. The path to the deployment file +Optional. The path to the template used for the deployment(s). Used to determine the level/scope (e.g. subscription). Required if deploymentName(s) are provided. .PARAMETER RemovalSequence Optional. The order of resource types to apply for deletion @@ -39,10 +42,13 @@ function Remove-Deployment { [Parameter(Mandatory = $false)] [string] $ManagementGroupId, - [Parameter(Mandatory = $true)] - [string[]] $DeploymentNames, + [Parameter(Mandatory = $false)] + [string[]] $DeploymentNames = @(), + + [Parameter(Mandatory = $false)] + [string[]] $ResourceIds = @(), - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $false)] [string] $TemplateFilePath, [Parameter(Mandatory = $false)] @@ -63,36 +69,39 @@ function Remove-Deployment { process { $azContext = Get-AzContext - # Prepare data - # ============ - $deploymentScope = Get-ScopeOfTemplateFile -TemplateFilePath $TemplateFilePath + $deployedTargetResources = $ResourceIds - # Fundamental checks - if ($deploymentScope -eq 'resourcegroup' -and -not (Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction 'SilentlyContinue')) { - Write-Verbose "Resource group [$ResourceGroupName] does not exist (anymore). Skipping removal of its contained resources" -Verbose - return - } - - # Fetch deployments - # ================= - $deployedTargetResources = @() + if ($DeploymentNames.Count -gt 0) { + # Prepare data + # ============ + $deploymentScope = Get-ScopeOfTemplateFile -TemplateFilePath $TemplateFilePath - foreach ($deploymentName in $DeploymentNames) { - $deploymentsInputObject = @{ - Name = $deploymentName - Scope = $deploymentScope - } - if (-not [String]::IsNullOrEmpty($ResourceGroupName)) { - $deploymentsInputObject['resourceGroupName'] = $ResourceGroupName + # Fundamental checks + if ($deploymentScope -eq 'resourcegroup' -and -not (Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction 'SilentlyContinue')) { + Write-Verbose "Resource group [$ResourceGroupName] does not exist (anymore). Skipping removal of its contained resources" -Verbose + return } - if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { - $deploymentsInputObject['ManagementGroupId'] = $ManagementGroupId + + # Fetch deployments + # ================= + + foreach ($deploymentName in $DeploymentNames) { + $deploymentsInputObject = @{ + Name = $deploymentName + Scope = $deploymentScope + } + if (-not [String]::IsNullOrEmpty($ResourceGroupName)) { + $deploymentsInputObject['resourceGroupName'] = $ResourceGroupName + } + if (-not [String]::IsNullOrEmpty($ManagementGroupId)) { + $deploymentsInputObject['ManagementGroupId'] = $ManagementGroupId + } + $deployedTargetResources += Get-DeploymentTargetResourceList @deploymentsInputObject } - $deployedTargetResources += Get-DeploymentTargetResourceList @deploymentsInputObject - } - if ($deployedTargetResources.Count -eq 0) { - throw 'No deployment target resources found.' + if ($deployedTargetResources.Count -eq 0) { + throw 'No deployment target resources found.' + } } [array] $deployedTargetResources = $deployedTargetResources | Select-Object -Unique From fe07241b7b787353980b2dbe325110b0a39c10a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Gr=C3=A4f?= Date: Mon, 8 Apr 2024 20:32:28 +1000 Subject: [PATCH 02/66] feat: Add availability zone and edge zone support (#1609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.compute.disk](https://github.com/segraef/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml/badge.svg?branch=feat%2Fdisk)](https://github.com/segraef/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Fabio Masciotra Co-authored-by: Máté Barabás Co-authored-by: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Co-authored-by: Wenjie Yu(MSFT) <81678720+zedy-wj@users.noreply.github.com> Co-authored-by: zedy Co-authored-by: Alexander Sehr Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: Kris Baranek --- avm/res/compute/disk/README.md | 63 ++- avm/res/compute/disk/main.bicep | 23 +- avm/res/compute/disk/main.json | 507 ++++++++++++++++++ .../disk/tests/e2e/defaults/main.test.bicep | 1 + .../disk/tests/e2e/image/main.test.bicep | 1 + .../disk/tests/e2e/import/main.test.bicep | 1 + .../disk/tests/e2e/max/main.test.bicep | 3 +- .../tests/e2e/waf-aligned/main.test.bicep | 3 +- avm/res/compute/disk/version.json | 2 +- 9 files changed, 595 insertions(+), 9 deletions(-) create mode 100644 avm/res/compute/disk/main.json diff --git a/avm/res/compute/disk/README.md b/avm/res/compute/disk/README.md index a84e7810a8..4a99aec290 100644 --- a/avm/res/compute/disk/README.md +++ b/avm/res/compute/disk/README.md @@ -17,7 +17,7 @@ This module deploys a Compute Disk | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Compute/disks` | [2022-07-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-07-02/disks) | +| `Microsoft.Compute/disks` | [2023-10-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-10-02/disks) | ## Usage examples @@ -47,6 +47,7 @@ module disk 'br/public:avm/res/compute/disk:' = { name: 'diskDeployment' params: { // Required parameters + availabilityZone: 0 name: 'cdmin001' sku: 'Standard_LRS' // Non-required parameters @@ -69,6 +70,9 @@ module disk 'br/public:avm/res/compute/disk:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "availabilityZone": { + "value": 0 + }, "name": { "value": "cdmin001" }, @@ -103,6 +107,7 @@ module disk 'br/public:avm/res/compute/disk:' = { name: 'diskDeployment' params: { // Required parameters + availabilityZone: 0 name: 'cdimg001' sku: 'Standard_LRS' // Non-required parameters @@ -126,6 +131,9 @@ module disk 'br/public:avm/res/compute/disk:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "availabilityZone": { + "value": 0 + }, "name": { "value": "cdimg001" }, @@ -163,6 +171,7 @@ module disk 'br/public:avm/res/compute/disk:' = { name: 'diskDeployment' params: { // Required parameters + availabilityZone: 0 name: 'cdimp001' sku: 'Standard_LRS' // Non-required parameters @@ -187,6 +196,9 @@ module disk 'br/public:avm/res/compute/disk:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "availabilityZone": { + "value": 0 + }, "name": { "value": "cdimp001" }, @@ -227,8 +239,9 @@ module disk 'br/public:avm/res/compute/disk:' = { name: 'diskDeployment' params: { // Required parameters + availabilityZone: 2 name: 'cdmax001' - sku: 'UltraSSD_LRS' + sku: 'Premium_LRS' // Non-required parameters diskIOPSReadWrite: 500 diskMBpsReadWrite: 60 @@ -280,11 +293,14 @@ module disk 'br/public:avm/res/compute/disk:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "availabilityZone": { + "value": 2 + }, "name": { "value": "cdmax001" }, "sku": { - "value": "UltraSSD_LRS" + "value": "Premium_LRS" }, // Non-required parameters "diskIOPSReadWrite": { @@ -361,8 +377,9 @@ module disk 'br/public:avm/res/compute/disk:' = { name: 'diskDeployment' params: { // Required parameters + availabilityZone: 2 name: 'cdwaf001' - sku: 'UltraSSD_LRS' + sku: 'Premium_LRS' // Non-required parameters diskIOPSReadWrite: 500 diskMBpsReadWrite: 60 @@ -397,11 +414,14 @@ module disk 'br/public:avm/res/compute/disk:' = { "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "availabilityZone": { + "value": 2 + }, "name": { "value": "cdwaf001" }, "sku": { - "value": "UltraSSD_LRS" + "value": "Premium_LRS" }, // Non-required parameters "diskIOPSReadWrite": { @@ -452,6 +472,7 @@ module disk 'br/public:avm/res/compute/disk:' = { | Parameter | Type | Description | | :-- | :-- | :-- | +| [`availabilityZone`](#parameter-availabilityzone) | int | If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. | | [`name`](#parameter-name) | string | The name of the disk that is being created. | | [`sku`](#parameter-sku) | string | The disks sku name. Can be . | @@ -473,6 +494,7 @@ module disk 'br/public:avm/res/compute/disk:' = { | [`createOption`](#parameter-createoption) | string | Sources of a disk creation. | | [`diskIOPSReadWrite`](#parameter-diskiopsreadwrite) | int | The number of IOPS allowed for this disk; only settable for UltraSSD disks. | | [`diskMBpsReadWrite`](#parameter-diskmbpsreadwrite) | int | The bandwidth allowed for this disk; only settable for UltraSSD disks. | +| [`edgeZone`](#parameter-edgezone) | string | Specifies the Edge Zone within the Azure Region where this Managed Disk should exist. Changing this forces a new Managed Disk to be created. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`hyperVGeneration`](#parameter-hypervgeneration) | string | The hypervisor generation of the Virtual Machine. Applicable to OS disks only. | | [`imageReferenceId`](#parameter-imagereferenceid) | string | A relative uri containing either a Platform Image Repository or user image reference. | @@ -491,6 +513,22 @@ module disk 'br/public:avm/res/compute/disk:' = { | [`tags`](#parameter-tags) | object | Tags of the availability set resource. | | [`uploadSizeBytes`](#parameter-uploadsizebytes) | int | If create option is Upload, this is the size of the contents of the upload including the VHD footer. | +### Parameter: `availabilityZone` + +If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used. + +- Required: Yes +- Type: int +- Allowed: + ```Bicep + [ + 0 + 1 + 2 + 3 + ] + ``` + ### Parameter: `name` The name of the disk that is being created. @@ -612,6 +650,21 @@ The bandwidth allowed for this disk; only settable for UltraSSD disks. - Type: int - Default: `0` +### Parameter: `edgeZone` + +Specifies the Edge Zone within the Azure Region where this Managed Disk should exist. Changing this forces a new Managed Disk to be created. + +- Required: No +- Type: string +- Default: `''` +- Allowed: + ```Bicep + [ + '' + 'EdgeZone' + ] + ``` + ### Parameter: `enableTelemetry` Enable/Disable usage telemetry for module. diff --git a/avm/res/compute/disk/main.bicep b/avm/res/compute/disk/main.bicep index a4f07803e8..6935e74b5f 100644 --- a/avm/res/compute/disk/main.bicep +++ b/avm/res/compute/disk/main.bicep @@ -20,6 +20,13 @@ param location string = resourceGroup().location @description('Required. The disks sku name. Can be .') param sku string +@allowed([ + 'EdgeZone' + '' +]) +@description('Optional. Specifies the Edge Zone within the Azure Region where this Managed Disk should exist. Changing this forces a new Managed Disk to be created.') +param edgeZone string = '' + @allowed([ 'x64' 'Arm64' @@ -118,6 +125,15 @@ param publicNetworkAccess string = 'Disabled' @description('Optional. True if the image from which the OS disk is created supports accelerated networking.') param acceleratedNetwork bool = false +@description('Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used.') +@allowed([ + 0 + 1 + 2 + 3 +]) +param availabilityZone int + @description('Optional. The lock settings of the service.') param lock lockType @@ -183,13 +199,17 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = } } -resource disk 'Microsoft.Compute/disks@2022-07-02' = { +resource disk 'Microsoft.Compute/disks@2023-10-02' = { name: name location: location tags: tags sku: { name: sku } + extendedLocation: !empty(edgeZone) ? { + type: edgeZone + name: edgeZone + } : null properties: { burstingEnabled: burstingEnabled completionPercent: completionPercent @@ -223,6 +243,7 @@ resource disk 'Microsoft.Compute/disks@2022-07-02' = { } : {} } + zones: availabilityZone != 0 ? array(string(availabilityZone)) : null } resource disk_lock 'Microsoft.Authorization/locks@2020-05-01' = diff --git a/avm/res/compute/disk/main.json b/avm/res/compute/disk/main.json new file mode 100644 index 0000000000..3a754a36b6 --- /dev/null +++ b/avm/res/compute/disk/main.json @@ -0,0 +1,507 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "13557505070746246600" + }, + "name": "Compute Disks", + "description": "This module deploys a Compute Disk", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the disk that is being created." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Resource location." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "Standard_LRS", + "Premium_LRS", + "StandardSSD_LRS", + "UltraSSD_LRS", + "Premium_ZRS", + "Premium_ZRS", + "PremiumV2_LRS" + ], + "metadata": { + "description": "Required. The disks sku name. Can be ." + } + }, + "edgeZone": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "EdgeZone", + "" + ], + "metadata": { + "description": "Optional. Specifies the Edge Zone within the Azure Region where this Managed Disk should exist. Changing this forces a new Managed Disk to be created." + } + }, + "architecture": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "x64", + "Arm64", + "" + ], + "metadata": { + "description": "Optional. CPU architecture supported by an OS disk." + } + }, + "burstingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Set to true to enable bursting beyond the provisioned performance target of the disk." + } + }, + "completionPercent": { + "type": "int", + "defaultValue": 100, + "metadata": { + "description": "Optional. Percentage complete for the background copy when a resource is created via the CopyStart operation." + } + }, + "createOption": { + "type": "string", + "defaultValue": "Empty", + "allowedValues": [ + "Attach", + "Copy", + "CopyStart", + "Empty", + "FromImage", + "Import", + "ImportSecure", + "Restore", + "Upload", + "UploadPreparedSecure" + ], + "metadata": { + "description": "Optional. Sources of a disk creation." + } + }, + "imageReferenceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. A relative uri containing either a Platform Image Repository or user image reference." + } + }, + "logicalSectorSize": { + "type": "int", + "defaultValue": 4096, + "metadata": { + "description": "Optional. Logical sector size in bytes for Ultra disks. Supported values are 512 ad 4096." + } + }, + "securityDataUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If create option is ImportSecure, this is the URI of a blob to be imported into VM guest state." + } + }, + "sourceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If create option is Copy, this is the ARM ID of the source snapshot or disk." + } + }, + "sourceUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If create option is Import, this is the URI of a blob to be imported into a managed disk." + } + }, + "storageAccountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the storage account containing the blob to import as a disk. Required if create option is Import." + } + }, + "uploadSizeBytes": { + "type": "int", + "defaultValue": 20972032, + "metadata": { + "description": "Optional. If create option is Upload, this is the size of the contents of the upload including the VHD footer." + } + }, + "diskSizeGB": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Conditional. The size of the disk to create. Required if create option is Empty." + } + }, + "diskIOPSReadWrite": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The number of IOPS allowed for this disk; only settable for UltraSSD disks." + } + }, + "diskMBpsReadWrite": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The bandwidth allowed for this disk; only settable for UltraSSD disks." + } + }, + "hyperVGeneration": { + "type": "string", + "defaultValue": "V2", + "allowedValues": [ + "V1", + "V2" + ], + "metadata": { + "description": "Optional. The hypervisor generation of the Virtual Machine. Applicable to OS disks only." + } + }, + "maxShares": { + "type": "int", + "defaultValue": 1, + "metadata": { + "description": "Optional. The maximum number of VMs that can attach to the disk at the same time. Default value is 0." + } + }, + "networkAccessPolicy": { + "type": "string", + "defaultValue": "DenyAll", + "allowedValues": [ + "AllowAll", + "AllowPrivate", + "DenyAll" + ], + "metadata": { + "description": "Optional. Policy for accessing the disk via network." + } + }, + "optimizedForFrequentAttach": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Setting this property to true improves reliability and performance of data disks that are frequently (more than 5 times a day) by detached from one virtual machine and attached to another. This property should not be set for disks that are not detached and attached frequently as it causes the disks to not align with the fault domain of the virtual machine." + } + }, + "osType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Windows", + "Linux", + "" + ], + "metadata": { + "description": "Optional. Sources of a disk creation." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Policy for controlling export on the disk." + } + }, + "acceleratedNetwork": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. True if the image from which the OS disk is created supports accelerated networking." + } + }, + "availabilityZone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "metadata": { + "description": "Required. If set to 1, 2 or 3, the availability zone is hardcoded to that value. If zero, then availability zones are not used." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the availability set resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]", + "Disk Backup Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e5e47e6-65f7-47ef-90b5-e5dd4d455f24')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "Disk Restore Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b50d9833-a0cb-478e-945f-707fcc997c13')]", + "Disk Snapshot Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.compute-disk.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "disk": { + "type": "Microsoft.Compute/disks", + "apiVersion": "2023-10-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "extendedLocation": "[if(not(empty(parameters('edgeZone'))), createObject('type', parameters('edgeZone'), 'name', parameters('edgeZone')), null())]", + "properties": { + "burstingEnabled": "[parameters('burstingEnabled')]", + "completionPercent": "[parameters('completionPercent')]", + "creationData": { + "createOption": "[parameters('createOption')]", + "imageReference": "[if(equals(parameters('createOption'), 'FromImage'), createObject('id', parameters('imageReferenceId')), null())]", + "logicalSectorSize": "[if(contains(parameters('sku'), 'Ultra'), parameters('logicalSectorSize'), null())]", + "securityDataUri": "[if(equals(parameters('createOption'), 'ImportSecure'), parameters('securityDataUri'), null())]", + "sourceResourceId": "[if(equals(parameters('createOption'), 'Copy'), parameters('sourceResourceId'), null())]", + "sourceUri": "[if(equals(parameters('createOption'), 'Import'), parameters('sourceUri'), null())]", + "storageAccountId": "[if(equals(parameters('createOption'), 'Import'), parameters('storageAccountId'), null())]", + "uploadSizeBytes": "[if(equals(parameters('createOption'), 'Upload'), parameters('uploadSizeBytes'), null())]" + }, + "diskIOPSReadWrite": "[if(contains(parameters('sku'), 'Ultra'), parameters('diskIOPSReadWrite'), null())]", + "diskMBpsReadWrite": "[if(contains(parameters('sku'), 'Ultra'), parameters('diskMBpsReadWrite'), null())]", + "diskSizeGB": "[if(equals(parameters('createOption'), 'Empty'), parameters('diskSizeGB'), null())]", + "hyperVGeneration": "[if(not(empty(parameters('osType'))), parameters('hyperVGeneration'), null())]", + "maxShares": "[parameters('maxShares')]", + "networkAccessPolicy": "[parameters('networkAccessPolicy')]", + "optimizedForFrequentAttach": "[parameters('optimizedForFrequentAttach')]", + "osType": "[if(not(empty(parameters('osType'))), parameters('osType'), null())]", + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "supportedCapabilities": "[if(not(empty(parameters('osType'))), createObject('acceleratedNetwork', parameters('acceleratedNetwork'), 'architecture', if(not(empty(parameters('architecture'))), parameters('architecture'), null())), createObject())]" + }, + "zones": "[if(not(equals(parameters('availabilityZone'), 0)), array(string(parameters('availabilityZone'))), null())]" + }, + "disk_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Compute/disks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "disk" + ] + }, + "disk_roleAssignments": { + "copy": { + "name": "disk_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/disks/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/disks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "disk" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the disk was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the disk." + }, + "value": "[resourceId('Microsoft.Compute/disks', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the disk." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('disk', '2023-10-02', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/compute/disk/tests/e2e/defaults/main.test.bicep b/avm/res/compute/disk/tests/e2e/defaults/main.test.bicep index 1e04040508..719807debb 100644 --- a/avm/res/compute/disk/tests/e2e/defaults/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/defaults/main.test.bicep @@ -43,6 +43,7 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}-${serviceShort}001' location: resourceLocation sku: 'Standard_LRS' + availabilityZone: 0 diskSizeGB: 1 } } diff --git a/avm/res/compute/disk/tests/e2e/image/main.test.bicep b/avm/res/compute/disk/tests/e2e/image/main.test.bicep index 69c4899486..968fa356b8 100644 --- a/avm/res/compute/disk/tests/e2e/image/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/image/main.test.bicep @@ -52,6 +52,7 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}-${serviceShort}001' location: resourceLocation sku: 'Standard_LRS' + availabilityZone: 0 createOption: 'FromImage' imageReferenceId: '${subscription().id}/Providers/Microsoft.Compute/Locations/westeurope/Publishers/MicrosoftWindowsServer/ArtifactTypes/VMImage/Offers/WindowsServer/Skus/2022-datacenter-azure-edition/Versions/20348.2340.240303' } diff --git a/avm/res/compute/disk/tests/e2e/import/main.test.bicep b/avm/res/compute/disk/tests/e2e/import/main.test.bicep index 6acc81a6dd..ec552e0452 100644 --- a/avm/res/compute/disk/tests/e2e/import/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/import/main.test.bicep @@ -59,6 +59,7 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}-${serviceShort}001' location: resourceLocation sku: 'Standard_LRS' + availabilityZone: 0 createOption: 'Import' sourceUri: nestedDependencies.outputs.vhdUri storageAccountId: nestedDependencies.outputs.storageAccountResourceId diff --git a/avm/res/compute/disk/tests/e2e/max/main.test.bicep b/avm/res/compute/disk/tests/e2e/max/main.test.bicep index 0bbafb98e5..33c8b5bd54 100644 --- a/avm/res/compute/disk/tests/e2e/max/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/max/main.test.bicep @@ -51,7 +51,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}-${serviceShort}001' location: resourceLocation - sku: 'UltraSSD_LRS' + sku: 'Premium_LRS' + availabilityZone: 2 diskIOPSReadWrite: 500 diskMBpsReadWrite: 60 diskSizeGB: 128 diff --git a/avm/res/compute/disk/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/disk/tests/e2e/waf-aligned/main.test.bicep index b6f4589566..52e752175a 100644 --- a/avm/res/compute/disk/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/compute/disk/tests/e2e/waf-aligned/main.test.bicep @@ -42,7 +42,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}-${serviceShort}001' location: resourceLocation - sku: 'UltraSSD_LRS' + sku: 'Premium_LRS' + availabilityZone: 2 diskIOPSReadWrite: 500 diskMBpsReadWrite: 60 diskSizeGB: 128 diff --git a/avm/res/compute/disk/version.json b/avm/res/compute/disk/version.json index 7fa401bdf7..9481fea58e 100644 --- a/avm/res/compute/disk/version.json +++ b/avm/res/compute/disk/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] From af4a87114e208f39aceb709b454fc5fe861eb2a8 Mon Sep 17 00:00:00 2001 From: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:12:20 +0100 Subject: [PATCH 03/66] fix: serviceBus resource name length limits (#1539) ## Description Fix to min & max length limits for resource names Closes #1438 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.service-bus.namespace](https://github.com/ChrisSidebotham/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=servicebus-bug-1438)](https://github.com/ChrisSidebotham/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../namespace/authorization-rule/main.bicep | 4 +- .../namespace/authorization-rule/main.json | 6 +-- .../disaster-recovery-config/main.bicep | 4 +- .../disaster-recovery-config/main.json | 6 +-- avm/res/service-bus/namespace/main.bicep | 2 +- avm/res/service-bus/namespace/main.json | 48 +++++++++---------- .../migration-configuration/main.bicep | 4 +- .../migration-configuration/main.json | 6 +-- .../namespace/network-rule-set/main.bicep | 4 +- .../namespace/network-rule-set/main.json | 6 +-- .../service-bus/namespace/queue/main.bicep | 8 ++-- avm/res/service-bus/namespace/queue/main.json | 10 ++-- .../service-bus/namespace/topic/main.bicep | 8 ++-- avm/res/service-bus/namespace/topic/main.json | 10 ++-- 14 files changed, 63 insertions(+), 63 deletions(-) diff --git a/avm/res/service-bus/namespace/authorization-rule/main.bicep b/avm/res/service-bus/namespace/authorization-rule/main.bicep index 3a3c044164..909fa5c73c 100644 --- a/avm/res/service-bus/namespace/authorization-rule/main.bicep +++ b/avm/res/service-bus/namespace/authorization-rule/main.bicep @@ -3,8 +3,8 @@ metadata description = 'This module deploys a Service Bus Namespace Authorizatio metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param namespaceName string @description('Required. The name of the authorization rule.') diff --git a/avm/res/service-bus/namespace/authorization-rule/main.json b/avm/res/service-bus/namespace/authorization-rule/main.json index 213b672df5..a40a89b354 100644 --- a/avm/res/service-bus/namespace/authorization-rule/main.json +++ b/avm/res/service-bus/namespace/authorization-rule/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "17436716625562680995" + "templateHash": "15856701624247874001" }, "name": "Service Bus Namespace Authorization Rules", "description": "This module deploys a Service Bus Namespace Authorization Rule.", @@ -14,8 +14,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } diff --git a/avm/res/service-bus/namespace/disaster-recovery-config/main.bicep b/avm/res/service-bus/namespace/disaster-recovery-config/main.bicep index dea2e3397f..b110952bc1 100644 --- a/avm/res/service-bus/namespace/disaster-recovery-config/main.bicep +++ b/avm/res/service-bus/namespace/disaster-recovery-config/main.bicep @@ -3,8 +3,8 @@ metadata description = 'This module deploys a Service Bus Namespace Disaster Rec metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param namespaceName string @description('Optional. The name of the disaster recovery config.') diff --git a/avm/res/service-bus/namespace/disaster-recovery-config/main.json b/avm/res/service-bus/namespace/disaster-recovery-config/main.json index 7269a6568a..c2c2606880 100644 --- a/avm/res/service-bus/namespace/disaster-recovery-config/main.json +++ b/avm/res/service-bus/namespace/disaster-recovery-config/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5141733056602320076" + "templateHash": "3332271240583753856" }, "name": "Service Bus Namespace Disaster Recovery Configs", "description": "This module deploys a Service Bus Namespace Disaster Recovery Config", @@ -14,8 +14,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index c5cccbe2ac..8f3a7c22ab 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -3,7 +3,7 @@ metadata description = 'This module deploys a Service Bus Namespace.' metadata owner = 'Azure/module-maintainers' @description('Required. Name of the Service Bus Namespace.') -@maxLength(50) +@maxLength(260) param name string @description('Optional. Location for all resources.') diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index 7d3cb4e282..e9e7ee32e7 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "6479170874747722696" + "templateHash": "12101686609428052773" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace.", @@ -1086,7 +1086,7 @@ "parameters": { "name": { "type": "string", - "maxLength": 50, + "maxLength": 260, "metadata": { "description": "Required. Name of the Service Bus Namespace." } @@ -1462,7 +1462,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "17436716625562680995" + "templateHash": "15856701624247874001" }, "name": "Service Bus Namespace Authorization Rules", "description": "This module deploys a Service Bus Namespace Authorization Rule.", @@ -1471,8 +1471,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } @@ -1566,7 +1566,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5141733056602320076" + "templateHash": "3332271240583753856" }, "name": "Service Bus Namespace Disaster Recovery Configs", "description": "This module deploys a Service Bus Namespace Disaster Recovery Config", @@ -1575,8 +1575,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } @@ -1671,7 +1671,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "15192198614426327006" + "templateHash": "3812335497838251447" }, "name": "Service Bus Namespace Migration Configuration", "description": "This module deploys a Service Bus Namespace Migration Configuration.", @@ -1680,8 +1680,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } @@ -1776,7 +1776,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "4933314100479664510" + "templateHash": "7745744247225628529" }, "name": "Service Bus Namespace Network Rule Sets", "description": "This module deploys a ServiceBus Namespace Network Rule Set.", @@ -1785,8 +1785,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Network Rule Set. Required if the template is used in a standalone deployment." } @@ -1976,7 +1976,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5679821968845040041" + "templateHash": "6465956085720113527" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue.", @@ -2078,16 +2078,16 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Required. Name of the Service Bus Queue." } @@ -2542,7 +2542,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "2743713566937430307" + "templateHash": "10365585985239248264" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic.", @@ -2789,16 +2789,16 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Topic. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Required. Name of the Service Bus Topic." } diff --git a/avm/res/service-bus/namespace/migration-configuration/main.bicep b/avm/res/service-bus/namespace/migration-configuration/main.bicep index c48644c256..39709565a8 100644 --- a/avm/res/service-bus/namespace/migration-configuration/main.bicep +++ b/avm/res/service-bus/namespace/migration-configuration/main.bicep @@ -3,8 +3,8 @@ metadata description = 'This module deploys a Service Bus Namespace Migration Co metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param namespaceName string @description('Required. Name to access Standard Namespace after migration.') diff --git a/avm/res/service-bus/namespace/migration-configuration/main.json b/avm/res/service-bus/namespace/migration-configuration/main.json index 5949d14b2e..15a83a63cc 100644 --- a/avm/res/service-bus/namespace/migration-configuration/main.json +++ b/avm/res/service-bus/namespace/migration-configuration/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "15192198614426327006" + "templateHash": "3812335497838251447" }, "name": "Service Bus Namespace Migration Configuration", "description": "This module deploys a Service Bus Namespace Migration Configuration.", @@ -14,8 +14,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } diff --git a/avm/res/service-bus/namespace/network-rule-set/main.bicep b/avm/res/service-bus/namespace/network-rule-set/main.bicep index ac842a0442..b2fa51cb5e 100644 --- a/avm/res/service-bus/namespace/network-rule-set/main.bicep +++ b/avm/res/service-bus/namespace/network-rule-set/main.bicep @@ -3,8 +3,8 @@ metadata description = 'This module deploys a ServiceBus Namespace Network Rule metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Network Rule Set. Required if the template is used in a standalone deployment.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param namespaceName string @allowed([ diff --git a/avm/res/service-bus/namespace/network-rule-set/main.json b/avm/res/service-bus/namespace/network-rule-set/main.json index aa8079beeb..46f4c8e9ae 100644 --- a/avm/res/service-bus/namespace/network-rule-set/main.json +++ b/avm/res/service-bus/namespace/network-rule-set/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "4933314100479664510" + "templateHash": "7745744247225628529" }, "name": "Service Bus Namespace Network Rule Sets", "description": "This module deploys a ServiceBus Namespace Network Rule Set.", @@ -14,8 +14,8 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Network Rule Set. Required if the template is used in a standalone deployment." } diff --git a/avm/res/service-bus/namespace/queue/main.bicep b/avm/res/service-bus/namespace/queue/main.bicep index 520bd73926..1d924b040c 100644 --- a/avm/res/service-bus/namespace/queue/main.bicep +++ b/avm/res/service-bus/namespace/queue/main.bicep @@ -3,13 +3,13 @@ metadata description = 'This module deploys a Service Bus Namespace Queue.' metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param namespaceName string @description('Required. Name of the Service Bus Queue.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param name string @description('Optional. ISO 8061 timeSpan idle interval after which the queue is automatically deleted. The minimum duration is 5 minutes (PT5M).') diff --git a/avm/res/service-bus/namespace/queue/main.json b/avm/res/service-bus/namespace/queue/main.json index bca571c612..16b71c7187 100644 --- a/avm/res/service-bus/namespace/queue/main.json +++ b/avm/res/service-bus/namespace/queue/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5679821968845040041" + "templateHash": "6465956085720113527" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue.", @@ -108,16 +108,16 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Queue. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Required. Name of the Service Bus Queue." } diff --git a/avm/res/service-bus/namespace/topic/main.bicep b/avm/res/service-bus/namespace/topic/main.bicep index aafb8dfc01..a4bad8e53a 100644 --- a/avm/res/service-bus/namespace/topic/main.bicep +++ b/avm/res/service-bus/namespace/topic/main.bicep @@ -3,13 +3,13 @@ metadata description = 'This module deploys a Service Bus Namespace Topic.' metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Service Bus Namespace for the Service Bus Topic. Required if the template is used in a standalone deployment.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param namespaceName string @description('Required. Name of the Service Bus Topic.') -@minLength(6) -@maxLength(50) +@minLength(1) +@maxLength(260) param name string @description('Optional. The maximum size of the topic in megabytes, which is the size of memory allocated for the topic. Default is 1024.') diff --git a/avm/res/service-bus/namespace/topic/main.json b/avm/res/service-bus/namespace/topic/main.json index 1bbf1eca82..e5db6e691e 100644 --- a/avm/res/service-bus/namespace/topic/main.json +++ b/avm/res/service-bus/namespace/topic/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "2743713566937430307" + "templateHash": "10365585985239248264" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic.", @@ -253,16 +253,16 @@ "parameters": { "namespaceName": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Conditional. The name of the parent Service Bus Namespace for the Service Bus Topic. Required if the template is used in a standalone deployment." } }, "name": { "type": "string", - "minLength": 6, - "maxLength": 50, + "minLength": 1, + "maxLength": 260, "metadata": { "description": "Required. Name of the Service Bus Topic." } From ab58393233910cfa45c6f3205957ee5be3af48ff Mon Sep 17 00:00:00 2001 From: Clint Grove <30802291+clintgrove@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:35:33 +0000 Subject: [PATCH 04/66] fix: Namefix for integration runtime `/avm/res/data-factory/factory /integration-runtime/` (#1616) ## Description a Simple name correction in spelling of the Integration Runtime passthrough parameter ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.data-factory.factory](https://github.com/clintgrove/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=integrationRuntime-namefix)](https://github.com/clintgrove/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../factory/integration-runtime/README.md | 4 ++-- .../factory/integration-runtime/main.bicep | 20 ++++++++++--------- .../factory/integration-runtime/main.json | 6 +++--- avm/res/data-factory/factory/main.bicep | 2 +- avm/res/data-factory/factory/main.json | 12 +++++------ 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/avm/res/data-factory/factory/integration-runtime/README.md b/avm/res/data-factory/factory/integration-runtime/README.md index 910743c3a9..5cdeec1c00 100644 --- a/avm/res/data-factory/factory/integration-runtime/README.md +++ b/avm/res/data-factory/factory/integration-runtime/README.md @@ -36,7 +36,7 @@ This module deploys a Data Factory Managed or Self-Hosted Integration Runtime. | Parameter | Type | Description | | :-- | :-- | :-- | -| [`interagrationRuntimeCustomDescription`](#parameter-interagrationruntimecustomdescription) | string | The description of the Integration Runtime. | +| [`integrationRuntimeCustomDescription`](#parameter-integrationruntimecustomdescription) | string | The description of the Integration Runtime. | | [`managedVirtualNetworkName`](#parameter-managedvirtualnetworkname) | string | The name of the Managed Virtual Network if using type "Managed" . | | [`typeProperties`](#parameter-typeproperties) | object | Integration Runtime type properties. Required if type is "Managed". | @@ -68,7 +68,7 @@ The name of the parent Azure Data Factory. Required if the template is used in a - Required: Yes - Type: string -### Parameter: `interagrationRuntimeCustomDescription` +### Parameter: `integrationRuntimeCustomDescription` The description of the Integration Runtime. diff --git a/avm/res/data-factory/factory/integration-runtime/main.bicep b/avm/res/data-factory/factory/integration-runtime/main.bicep index 2c2e252d4e..1c6d344fb9 100644 --- a/avm/res/data-factory/factory/integration-runtime/main.bicep +++ b/avm/res/data-factory/factory/integration-runtime/main.bicep @@ -22,7 +22,7 @@ param managedVirtualNetworkName string = '' param typeProperties object = {} @description('Optional. The description of the Integration Runtime.') -param interagrationRuntimeCustomDescription string = 'Managed Integration Runtime created by avm-res-datafactory-factories' +param integrationRuntimeCustomDescription string = 'Managed Integration Runtime created by avm-res-datafactory-factories' var managedVirtualNetworkVar = { referenceName: type == 'Managed' ? managedVirtualNetworkName : null @@ -36,14 +36,16 @@ resource dataFactory 'Microsoft.DataFactory/factories@2018-06-01' existing = { resource integrationRuntime 'Microsoft.DataFactory/factories/integrationRuntimes@2018-06-01' = { name: name parent: dataFactory - properties: type == 'Managed' ? { - description: interagrationRuntimeCustomDescription - type: type - managedVirtualNetwork: managedVirtualNetworkVar - typeProperties: typeProperties - } : { - type: type - } + properties: type == 'Managed' + ? { + description: integrationRuntimeCustomDescription + type: type + managedVirtualNetwork: managedVirtualNetworkVar + typeProperties: typeProperties + } + : { + type: type + } } @description('The name of the Resource Group the Integration Runtime was created in.') diff --git a/avm/res/data-factory/factory/integration-runtime/main.json b/avm/res/data-factory/factory/integration-runtime/main.json index 25dade0fb5..1ddb359e15 100644 --- a/avm/res/data-factory/factory/integration-runtime/main.json +++ b/avm/res/data-factory/factory/integration-runtime/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5995255380692588407" + "templateHash": "6019779622358376532" }, "name": "Data Factory Integration RunTimes", "description": "This module deploys a Data Factory Managed or Self-Hosted Integration Runtime.", @@ -48,7 +48,7 @@ "description": "Optional. Integration Runtime type properties. Required if type is \"Managed\"." } }, - "interagrationRuntimeCustomDescription": { + "integrationRuntimeCustomDescription": { "type": "string", "defaultValue": "Managed Integration Runtime created by avm-res-datafactory-factories", "metadata": { @@ -67,7 +67,7 @@ "type": "Microsoft.DataFactory/factories/integrationRuntimes", "apiVersion": "2018-06-01", "name": "[format('{0}/{1}', parameters('dataFactoryName'), parameters('name'))]", - "properties": "[if(equals(parameters('type'), 'Managed'), createObject('description', parameters('interagrationRuntimeCustomDescription'), 'type', parameters('type'), 'managedVirtualNetwork', variables('managedVirtualNetworkVar'), 'typeProperties', parameters('typeProperties')), createObject('type', parameters('type')))]" + "properties": "[if(equals(parameters('type'), 'Managed'), createObject('description', parameters('integrationRuntimeCustomDescription'), 'type', parameters('type'), 'managedVirtualNetwork', variables('managedVirtualNetworkVar'), 'typeProperties', parameters('typeProperties')), createObject('type', parameters('type')))]" } ], "outputs": { diff --git a/avm/res/data-factory/factory/main.bicep b/avm/res/data-factory/factory/main.bicep index 8d4267f055..c82317923d 100644 --- a/avm/res/data-factory/factory/main.bicep +++ b/avm/res/data-factory/factory/main.bicep @@ -217,7 +217,7 @@ module dataFactory_integrationRuntimes 'integration-runtime/main.bicep' = [ dataFactoryName: dataFactory.name name: integrationRuntime.name type: integrationRuntime.type - interagrationRuntimeCustomDescription: integrationRuntime.?interagrationRuntimeCustomDescription ?? 'Managed Integration Runtime created by avm-res-datafactory-factories' + integrationRuntimeCustomDescription: integrationRuntime.?integrationRuntimeCustomDescription ?? 'Managed Integration Runtime created by avm-res-datafactory-factories' managedVirtualNetworkName: contains(integrationRuntime, 'managedVirtualNetworkName') ? integrationRuntime.managedVirtualNetworkName : '' diff --git a/avm/res/data-factory/factory/main.json b/avm/res/data-factory/factory/main.json index 84e0e21e85..aad72cb4c9 100644 --- a/avm/res/data-factory/factory/main.json +++ b/avm/res/data-factory/factory/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "13420826412692086400" + "templateHash": "14187397977187427796" }, "name": "Data Factories", "description": "This module deploys a Data Factory.", @@ -1033,8 +1033,8 @@ "type": { "value": "[parameters('integrationRuntimes')[copyIndex()].type]" }, - "interagrationRuntimeCustomDescription": { - "value": "[coalesce(tryGet(parameters('integrationRuntimes')[copyIndex()], 'interagrationRuntimeCustomDescription'), 'Managed Integration Runtime created by avm-res-datafactory-factories')]" + "integrationRuntimeCustomDescription": { + "value": "[coalesce(tryGet(parameters('integrationRuntimes')[copyIndex()], 'integrationRuntimeCustomDescription'), 'Managed Integration Runtime created by avm-res-datafactory-factories')]" }, "managedVirtualNetworkName": "[if(contains(parameters('integrationRuntimes')[copyIndex()], 'managedVirtualNetworkName'), createObject('value', parameters('integrationRuntimes')[copyIndex()].managedVirtualNetworkName), createObject('value', ''))]", "typeProperties": "[if(contains(parameters('integrationRuntimes')[copyIndex()], 'typeProperties'), createObject('value', parameters('integrationRuntimes')[copyIndex()].typeProperties), createObject('value', createObject()))]" @@ -1046,7 +1046,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5995255380692588407" + "templateHash": "6019779622358376532" }, "name": "Data Factory Integration RunTimes", "description": "This module deploys a Data Factory Managed or Self-Hosted Integration Runtime.", @@ -1089,7 +1089,7 @@ "description": "Optional. Integration Runtime type properties. Required if type is \"Managed\"." } }, - "interagrationRuntimeCustomDescription": { + "integrationRuntimeCustomDescription": { "type": "string", "defaultValue": "Managed Integration Runtime created by avm-res-datafactory-factories", "metadata": { @@ -1108,7 +1108,7 @@ "type": "Microsoft.DataFactory/factories/integrationRuntimes", "apiVersion": "2018-06-01", "name": "[format('{0}/{1}', parameters('dataFactoryName'), parameters('name'))]", - "properties": "[if(equals(parameters('type'), 'Managed'), createObject('description', parameters('interagrationRuntimeCustomDescription'), 'type', parameters('type'), 'managedVirtualNetwork', variables('managedVirtualNetworkVar'), 'typeProperties', parameters('typeProperties')), createObject('type', parameters('type')))]" + "properties": "[if(equals(parameters('type'), 'Managed'), createObject('description', parameters('integrationRuntimeCustomDescription'), 'type', parameters('type'), 'managedVirtualNetwork', variables('managedVirtualNetworkVar'), 'typeProperties', parameters('typeProperties')), createObject('type', parameters('type')))]" } ], "outputs": { From bf90b18b6254a0eee1c62082409a063da035b282 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Mon, 8 Apr 2024 19:58:56 +0100 Subject: [PATCH 05/66] fix: nat-gateway singular workflow name (#1619) ## Description ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.nat-gateway](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml/badge.svg?branch=fix%2Fworkflow-name)](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml) | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/workflows/avm.res.network.nat-gateway.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/avm.res.network.nat-gateway.yml b/.github/workflows/avm.res.network.nat-gateway.yml index 9225b5fa7d..34dd4c8ff9 100644 --- a/.github/workflows/avm.res.network.nat-gateway.yml +++ b/.github/workflows/avm.res.network.nat-gateway.yml @@ -1,4 +1,4 @@ -name: "avm.res.network.nat-gateways" +name: "avm.res.network.nat-gateway" on: schedule: From 0d918aa12fd7b88aa0168c11dda5fa8b2c2aaf08 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:13:59 +0100 Subject: [PATCH 06/66] fix: data-factory and databricks missing v2 formatting (#1624) ## Description ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.data-factory.factory](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=fix%2Fmissing-v2-formatting)](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml) | | [![avm.res.databricks.workspace](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=fix%2Fmissing-v2-formatting)](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../managed-virtual-network/main.bicep | 22 +- .../tests/e2e/defaults/main.test.bicep | 16 +- .../factory/tests/e2e/max/dependencies.bicep | 5 +- .../factory/tests/e2e/max/main.test.bicep | 213 +++++++++--------- .../tests/e2e/waf-aligned/main.test.bicep | 58 ++--- .../tests/e2e/defaults/main.test.bicep | 16 +- 6 files changed, 173 insertions(+), 157 deletions(-) diff --git a/avm/res/data-factory/factory/managed-virtual-network/main.bicep b/avm/res/data-factory/factory/managed-virtual-network/main.bicep index c8b4ff7f12..9faf5cf01b 100644 --- a/avm/res/data-factory/factory/managed-virtual-network/main.bicep +++ b/avm/res/data-factory/factory/managed-virtual-network/main.bicep @@ -21,17 +21,19 @@ resource managedVirtualNetwork 'Microsoft.DataFactory/factories/managedVirtualNe properties: {} } -module managedVirtualNetwork_managedPrivateEndpoint 'managed-private-endpoint/main.bicep' = [for (managedPrivateEndpoint, index) in managedPrivateEndpoints: { - name: '${deployment().name}-managedPrivateEndpoint-${index}' - params: { - dataFactoryName: dataFactoryName - managedVirtualNetworkName: name - name: managedPrivateEndpoint.name - fqdns: managedPrivateEndpoint.fqdns - groupId: managedPrivateEndpoint.groupId - privateLinkResourceId: managedPrivateEndpoint.privateLinkResourceId +module managedVirtualNetwork_managedPrivateEndpoint 'managed-private-endpoint/main.bicep' = [ + for (managedPrivateEndpoint, index) in managedPrivateEndpoints: { + name: '${deployment().name}-managedPrivateEndpoint-${index}' + params: { + dataFactoryName: dataFactoryName + managedVirtualNetworkName: name + name: managedPrivateEndpoint.name + fqdns: managedPrivateEndpoint.fqdns + groupId: managedPrivateEndpoint.groupId + privateLinkResourceId: managedPrivateEndpoint.privateLinkResourceId + } } -}] +] @description('The name of the Resource Group the Managed Virtual Network was created in.') output resourceGroupName string = resourceGroup().name diff --git a/avm/res/data-factory/factory/tests/e2e/defaults/main.test.bicep b/avm/res/data-factory/factory/tests/e2e/defaults/main.test.bicep index 2946e76de7..831ae4557f 100644 --- a/avm/res/data-factory/factory/tests/e2e/defaults/main.test.bicep +++ b/avm/res/data-factory/factory/tests/e2e/defaults/main.test.bicep @@ -36,11 +36,13 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + } } -}] +] diff --git a/avm/res/data-factory/factory/tests/e2e/max/dependencies.bicep b/avm/res/data-factory/factory/tests/e2e/max/dependencies.bicep index a6ab43ad7a..8e8339f340 100644 --- a/avm/res/data-factory/factory/tests/e2e/max/dependencies.bicep +++ b/avm/res/data-factory/factory/tests/e2e/max/dependencies.bicep @@ -82,7 +82,10 @@ resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { properties: { principalId: managedIdentity.properties.principalId // Key Vault Crypto User - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) principalType: 'ServicePrincipal' } } diff --git a/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep b/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep index 541f329d93..7eeb7382b7 100644 --- a/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep +++ b/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep @@ -62,119 +62,124 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - customerManagedKey: { - keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName - keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId - userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId - } - diagnosticSettings: [ - { - name: 'customSetting' - metricCategories: [ - { - category: 'AllMetrics' - } - ] - eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName - eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId - storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId - workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId } - ] - gitConfigureLater: true - globalParameters: { - testParameter1: { - type: 'String' - value: 'testValue1' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + gitConfigureLater: true + globalParameters: { + testParameter1: { + type: 'String' + value: 'testValue1' + } } - } - integrationRuntimes: [ - { - managedVirtualNetworkName: 'default' - name: 'AutoResolveIntegrationRuntime' - type: 'Managed' - typeProperties: { - computeProperties: { - location: 'AutoResolve' + integrationRuntimes: [ + { + managedVirtualNetworkName: 'default' + name: 'AutoResolveIntegrationRuntime' + type: 'Managed' + typeProperties: { + computeProperties: { + location: 'AutoResolve' + } } } - } - { - name: 'TestRuntime' - type: 'SelfHosted' - } - ] - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } - managedPrivateEndpoints: [ - { - fqdns: [ - nestedDependencies.outputs.storageAccountBlobEndpoint - ] - groupId: 'blob' - name: '${nestedDependencies.outputs.storageAccountName}-managed-privateEndpoint' - privateLinkResourceId: nestedDependencies.outputs.storageAccountResourceId - } - ] - managedVirtualNetworkName: 'default' - privateEndpoints: [ - { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId - ] - subnetResourceId: nestedDependencies.outputs.subnetResourceId - tags: { - 'hidden-title': 'This is visible in the resource name' - application: 'AVM' + { + name: 'TestRuntime' + type: 'SelfHosted' } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' } - { - privateDnsZoneResourceIds: [ - nestedDependencies.outputs.privateDNSZoneResourceId + managedPrivateEndpoints: [ + { + fqdns: [ + nestedDependencies.outputs.storageAccountBlobEndpoint + ] + groupId: 'blob' + name: '${nestedDependencies.outputs.storageAccountName}-managed-privateEndpoint' + privateLinkResourceId: nestedDependencies.outputs.storageAccountResourceId + } + ] + managedVirtualNetworkName: 'default' + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + application: 'AVM' + } + } + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId ] - subnetResourceId: nestedDependencies.outputs.subnetResourceId - } - ] - roleAssignments: [ - { - roleDefinitionIdOrName: 'Owner' - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } - { - roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' } - { - roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - principalId: nestedDependencies.outputs.managedIdentityPrincipalId - principalType: 'ServicePrincipal' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' } - ] - managedIdentities: { - systemAssigned: true - userAssignedResourceIds: [ - nestedDependencies.outputs.managedIdentityResourceId - ] - } - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] } - dependsOn: [ - nestedDependencies - diagnosticDependencies - ] -}] +] diff --git a/avm/res/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep b/avm/res/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep index 7e50560269..8662984c06 100644 --- a/avm/res/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/data-factory/factory/tests/e2e/waf-aligned/main.test.bicep @@ -50,34 +50,36 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation - diagnosticSettings: [ - { - eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName - eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId - storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId - workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + diagnosticSettings: [ + { + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + gitConfigureLater: true + integrationRuntimes: [ + { + name: 'TestRuntime' + type: 'SelfHosted' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' } - ] - gitConfigureLater: true - integrationRuntimes: [ - { - name: 'TestRuntime' - type: 'SelfHosted' - } - ] - tags: { - 'hidden-title': 'This is visible in the resource name' - Environment: 'Non-Prod' - Role: 'DeploymentValidation' } + dependsOn: [ + diagnosticDependencies + ] } - dependsOn: [ - diagnosticDependencies - ] -}] +] diff --git a/avm/res/databricks/workspace/tests/e2e/defaults/main.test.bicep b/avm/res/databricks/workspace/tests/e2e/defaults/main.test.bicep index daf25fffde..d7b8b471c1 100644 --- a/avm/res/databricks/workspace/tests/e2e/defaults/main.test.bicep +++ b/avm/res/databricks/workspace/tests/e2e/defaults/main.test.bicep @@ -36,11 +36,13 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { // ============== // @batchSize(1) -module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { - scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' - params: { - name: '${namePrefix}${serviceShort}001' - location: resourceLocation +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + } } -}] +] From d576709eca7eccf579b852c84bcf956e473c41aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Gr=C3=A4f?= Date: Tue, 9 Apr 2024 17:06:29 +1000 Subject: [PATCH 07/66] feat: RP update (#1623) --- avm/res/compute/disk-encryption-set/README.md | 2 +- avm/res/compute/disk-encryption-set/main.bicep | 2 +- avm/res/compute/disk-encryption-set/main.json | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/avm/res/compute/disk-encryption-set/README.md b/avm/res/compute/disk-encryption-set/README.md index 8cc108d0f5..7ccaba8451 100644 --- a/avm/res/compute/disk-encryption-set/README.md +++ b/avm/res/compute/disk-encryption-set/README.md @@ -17,7 +17,7 @@ This module deploys a Disk Encryption Set. The module will attempt to set permis | :-- | :-- | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | -| `Microsoft.Compute/diskEncryptionSets` | [2022-07-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-07-02/diskEncryptionSets) | +| `Microsoft.Compute/diskEncryptionSets` | [2023-10-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2023-10-02/diskEncryptionSets) | | `Microsoft.KeyVault/vaults/accessPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2022-07-01/vaults/accessPolicies) | ## Usage examples diff --git a/avm/res/compute/disk-encryption-set/main.bicep b/avm/res/compute/disk-encryption-set/main.bicep index 5932efeac2..2ddc1896a5 100644 --- a/avm/res/compute/disk-encryption-set/main.bicep +++ b/avm/res/compute/disk-encryption-set/main.bicep @@ -139,7 +139,7 @@ module keyVaultPermissions 'modules/nested_keyVaultPermissions.bicep' = [ } ] -resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2022-07-02' = { +resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2023-10-02' = { name: name location: location tags: tags diff --git a/avm/res/compute/disk-encryption-set/main.json b/avm/res/compute/disk-encryption-set/main.json index da2764ea80..db3719112f 100644 --- a/avm/res/compute/disk-encryption-set/main.json +++ b/avm/res/compute/disk-encryption-set/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "15675582132137222221" + "templateHash": "12741671665077521328" }, "name": "Disk Encryption Sets", "description": "This module deploys a Disk Encryption Set. The module will attempt to set permissions on the provided Key Vault for any used user-assigned identity.", @@ -279,7 +279,7 @@ }, "diskEncryptionSet": { "type": "Microsoft.Compute/diskEncryptionSets", - "apiVersion": "2022-07-02", + "apiVersion": "2023-10-02", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -711,14 +711,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('diskEncryptionSet', '2022-07-02', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('diskEncryptionSet', '2023-10-02', 'full'), 'identity'), 'principalId'), '')]" }, "identities": { "type": "object", "metadata": { "description": "The idenities of the disk encryption set." }, - "value": "[reference('diskEncryptionSet', '2022-07-02', 'full').identity]" + "value": "[reference('diskEncryptionSet', '2023-10-02', 'full').identity]" }, "keyVaultName": { "type": "string", @@ -732,7 +732,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('diskEncryptionSet', '2022-07-02', 'full').location]" + "value": "[reference('diskEncryptionSet', '2023-10-02', 'full').location]" } } -} \ No newline at end of file +} From 9c57abb4282f77f67bfef805bf6ed45c7e3e865f Mon Sep 17 00:00:00 2001 From: Mischa <94125469+mischavandenburg@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:48:16 +0200 Subject: [PATCH 08/66] feat: add securityContext to containerType for `avm/res/container-instance/container-group` (#1520) ## Description This adds the securityContext to the containerType. This makes it possible to configure capabilities, privilege escalation etc. Fixes #1514 ## Pipeline Reference | Pipeline | | -------- | | | I have some Service Principal trouble fixing the last step but the templates lint and deploy just fine: ![image](https://github.com/Azure/bicep-registry-modules/assets/94125469/73f4cb5e-7dcf-4a74-9d44-bfdd447ccd8f) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../container-group/README.md | 82 +++++++++++++++++++ .../container-group/main.bicep | 27 ++++++ .../container-group/main.json | 75 ++++++++++++++++- .../container-group/version.json | 10 +-- 4 files changed, 188 insertions(+), 6 deletions(-) diff --git a/avm/res/container-instance/container-group/README.md b/avm/res/container-instance/container-group/README.md index 3c26476be1..c93906a3f9 100644 --- a/avm/res/container-instance/container-group/README.md +++ b/avm/res/container-instance/container-group/README.md @@ -1021,6 +1021,7 @@ The resource requirements of the container instance. | Parameter | Type | Description | | :-- | :-- | :-- | | [`limits`](#parameter-containerspropertiesresourceslimits) | object | The resource limits of this container instance. | +| [`securityContext`](#parameter-containerspropertiesresourcessecuritycontext) | object | The security context of the container instance. | ### Parameter: `containers.properties.resources.requests` @@ -1162,6 +1163,87 @@ The memory limit in GB of this container instance. To specify a decimal value, u - Required: No - Type: int +### Parameter: `containers.properties.resources.securityContext` + +The security context of the container instance. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowPrivilegeEscalation`](#parameter-containerspropertiesresourcessecuritycontextallowprivilegeescalation) | bool | Whether privilege escalation is allowed for the container. | +| [`capabilities`](#parameter-containerspropertiesresourcessecuritycontextcapabilities) | object | The capabilities to add or drop for the container. | +| [`privileged`](#parameter-containerspropertiesresourcessecuritycontextprivileged) | bool | Whether the container is run in privileged mode. | +| [`runAsGroup`](#parameter-containerspropertiesresourcessecuritycontextrunasgroup) | int | The GID to run the container as. | +| [`runAsUser`](#parameter-containerspropertiesresourcessecuritycontextrunasuser) | int | The UID to run the container as. | +| [`seccompProfile`](#parameter-containerspropertiesresourcessecuritycontextseccompprofile) | string | The seccomp profile to use for the container. | + +### Parameter: `containers.properties.resources.securityContext.allowPrivilegeEscalation` + +Whether privilege escalation is allowed for the container. + +- Required: No +- Type: bool + +### Parameter: `containers.properties.resources.securityContext.capabilities` + +The capabilities to add or drop for the container. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`add`](#parameter-containerspropertiesresourcessecuritycontextcapabilitiesadd) | array | The list of capabilities to add. | +| [`drop`](#parameter-containerspropertiesresourcessecuritycontextcapabilitiesdrop) | array | The list of capabilities to drop. | + +### Parameter: `containers.properties.resources.securityContext.capabilities.add` + +The list of capabilities to add. + +- Required: No +- Type: array + +### Parameter: `containers.properties.resources.securityContext.capabilities.drop` + +The list of capabilities to drop. + +- Required: No +- Type: array + +### Parameter: `containers.properties.resources.securityContext.privileged` + +Whether the container is run in privileged mode. + +- Required: No +- Type: bool + +### Parameter: `containers.properties.resources.securityContext.runAsGroup` + +The GID to run the container as. + +- Required: No +- Type: int + +### Parameter: `containers.properties.resources.securityContext.runAsUser` + +The UID to run the container as. + +- Required: No +- Type: int + +### Parameter: `containers.properties.resources.securityContext.seccompProfile` + +The seccomp profile to use for the container. + +- Required: No +- Type: string + ### Parameter: `containers.properties.command` The command to execute within the container instance. diff --git a/avm/res/container-instance/container-group/main.bicep b/avm/res/container-instance/container-group/main.bicep index 55b8f22914..93289ff0e2 100644 --- a/avm/res/container-instance/container-group/main.bicep +++ b/avm/res/container-instance/container-group/main.bicep @@ -303,6 +303,33 @@ type containerType = { @description('Optional. The memory limit in GB of this container instance. To specify a decimal value, use the json() function.') memoryInGB: int? }? + + @description('Optional. The security context of the container instance.') + securityContext: { + @description('Optional. Whether privilege escalation is allowed for the container.') + allowPrivilegeEscalation: bool? + + @description('Optional. The capabilities to add or drop for the container.') + capabilities: { + @description('Optional. The list of capabilities to add.') + add: string[]? + + @description('Optional. The list of capabilities to drop.') + drop: string[]? + }? + + @description('Optional. Whether the container is run in privileged mode.') + privileged: bool? + + @description('Optional. The GID to run the container as.') + runAsGroup: int? + + @description('Optional. The UID to run the container as.') + runAsUser: int? + + @description('Optional. The seccomp profile to use for the container.') + seccompProfile: string? + }? } @description('Optional. The volume mounts within the container instance.') diff --git a/avm/res/container-instance/container-group/main.json b/avm/res/container-instance/container-group/main.json index 571886b43f..c96c58751f 100644 --- a/avm/res/container-instance/container-group/main.json +++ b/avm/res/container-instance/container-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "16935347611602196545" + "templateHash": "1114274520149552239" }, "name": "Container Instances Container Groups", "description": "This module deploys a Container Instance Container Group.", @@ -203,6 +203,79 @@ "metadata": { "description": "Optional. The resource limits of this container instance." } + }, + "securityContext": { + "type": "object", + "properties": { + "allowPrivilegeEscalation": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether privilege escalation is allowed for the container." + } + }, + "capabilities": { + "type": "object", + "properties": { + "add": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of capabilities to add." + } + }, + "drop": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of capabilities to drop." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The capabilities to add or drop for the container." + } + }, + "privileged": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the container is run in privileged mode." + } + }, + "runAsGroup": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The GID to run the container as." + } + }, + "runAsUser": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The UID to run the container as." + } + }, + "seccompProfile": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The seccomp profile to use for the container." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The security context of the container instance." + } } }, "metadata": { diff --git a/avm/res/container-instance/container-group/version.json b/avm/res/container-instance/container-group/version.json index 7fa401bdf7..daf1a794d9 100644 --- a/avm/res/container-instance/container-group/version.json +++ b/avm/res/container-instance/container-group/version.json @@ -1,7 +1,7 @@ { - "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", - "pathFilters": [ - "./main.json" - ] + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.2", + "pathFilters": [ + "./main.json" + ] } From df99b771e6882e40d41910b0de48c653f75ca044 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:54:03 +0200 Subject: [PATCH 09/66] Users/rahalan/fix pipeline (#1627) ## Description Not enough issues were fetched, so duplicate issues were created Fixes #1606 ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index 1983b7aed4..7a30bfa96d 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -41,7 +41,7 @@ function Set-AvmGithubIssueForWorkflow { [String[]] $IgnoreWorkflows = @() ) - $issues = gh issue list --state open --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --json 'title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 + $issues = gh issue list --state open --limit 500 --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --json 'title,url,body,comments,labels' --repo $Repo | ConvertFrom-Json -Depth 100 $runs = gh run list --json 'url,workflowName,headBranch,startedAt' --limit $LimitNumberOfRuns --repo $Repo | ConvertFrom-Json -Depth 100 $workflowRuns = @{} $issuesCreated = 0 From 7e12e0a3b7a89dcc29f70f56dc90bd169ebcff4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Pei=C3=9Fker?= <30857628+JPEasier@users.noreply.github.com> Date: Tue, 9 Apr 2024 21:17:42 +0200 Subject: [PATCH 10/66] fix: WAF Test for AKS Module - `avm/res/container-service/managed-cluster` (#1534) ## Description Update Test depolyments to match WAF rule set - Add default value for `autoUpgradeProfileUpgradeChannel` - Change the minimum agent count up to `3` ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/JPEasier/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=users%2Fjpeasier%2Favm-aks)](https://github.com/JPEasier/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] The bug was found by the module author, and no one has opened an issue to report it yet. ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Alexander Sehr --- .../managed-cluster/README.md | 42 +++++++++++++------ .../managed-cluster/main.bicep | 11 ++++- .../managed-cluster/main.json | 17 +++++++- .../tests/e2e/defaults/main.test.bicep | 2 +- .../tests/e2e/waf-aligned/main.test.bicep | 10 ++--- 5 files changed, 61 insertions(+), 21 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index 36e74a78b9..ec4fc5f535 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -562,7 +562,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster: Date: Tue, 9 Apr 2024 21:35:47 +0200 Subject: [PATCH 11/66] fix: add WAF rule set to container registry (#1631) ## Description Update test files for WAF rule set. Fixes #1527 Closes ##1527 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-registry.registry](https://github.com/JPEasier/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Fjpeasier%2Favm-container-registry)](https://github.com/JPEasier/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/container-registry/registry/README.md | 4 ++++ .../registry/tests/e2e/defaults/main.test.bicep | 1 + 2 files changed, 5 insertions(+) diff --git a/avm/res/container-registry/registry/README.md b/avm/res/container-registry/registry/README.md index cd49ff7b18..5d0fd4ae87 100644 --- a/avm/res/container-registry/registry/README.md +++ b/avm/res/container-registry/registry/README.md @@ -54,6 +54,7 @@ module registry 'br/public:avm/res/container-registry/registry:' = { // Required parameters name: 'crrmin001' // Non-required parameters + acrSku: 'Standard' location: '' } } @@ -76,6 +77,9 @@ module registry 'br/public:avm/res/container-registry/registry:' = { "value": "crrmin001" }, // Non-required parameters + "acrSku": { + "value": "Standard" + }, "location": { "value": "" } diff --git a/avm/res/container-registry/registry/tests/e2e/defaults/main.test.bicep b/avm/res/container-registry/registry/tests/e2e/defaults/main.test.bicep index 4913076429..09b3d228ac 100644 --- a/avm/res/container-registry/registry/tests/e2e/defaults/main.test.bicep +++ b/avm/res/container-registry/registry/tests/e2e/defaults/main.test.bicep @@ -43,6 +43,7 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: resourceLocation + acrSku: 'Standard' } } ] From 3bf7e40ea0b75a75e6706ef87fee2fbdc8d662dc Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Wed, 10 Apr 2024 08:19:37 +0100 Subject: [PATCH 12/66] feat: Skip test folder name for ptn (#1622) ## Description Closes #1470 - If the module is of type `ptn`, skip Pester test checking for `defaults` and `waf-aligned` test folders - Updating workflow job conditions accordingly. PsRule jobs running only on `defaults `and `waf-aligned` folders, need to be skipped if the value of `psRuleModuleTestFilePaths `is empty. `psRuleModuleTestFilePaths `is the variable calculated by the initialize pipeline job, hosting the path to the deploy test Test performed in a different branch (main) where a "fake" pattern module is created with no `defaults` or `waf-aligned` test folders. Pester tests for telemetry and orphaned modules have been commented during testing. PR 1620 contains, for reference, also the "fake" files used to test this update. 1. push to main - complete, success (publish runs) ![image](https://github.com/Azure/bicep-registry-modules/assets/56914614/75589a6d-30fe-40e4-b0f6-e24e813c8e16) 1. manual run - only static, success (publish is skipped) ![image](https://github.com/Azure/bicep-registry-modules/assets/56914614/7f28195a-b414-4f01-b5e4-62d43a282081) 1. manual run - only deployment (publish is skipped) ![image](https://github.com/Azure/bicep-registry-modules/assets/56914614/2e76bb48-3819-42a3-be65-637b46c48620) 1. push to main - complete, static fails (deployment and publish are skipped) ![image](https://github.com/Azure/bicep-registry-modules/assets/56914614/a09bc275-94d5-4d74-8496-2e4c82439537) 1. push to main - cancel pipeline while running static (deployment and publish are skipped) ![image](https://github.com/Azure/bicep-registry-modules/assets/56914614/c1bc0559-c084-42b4-9473-8b5cf0ee8d5b) 1. manual run - complete, success (publish runs) ![image](https://github.com/Azure/bicep-registry-modules/assets/56914614/8502e41a-41ed-4a6f-9681-4dbac97c36f8) ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.batch.batch-account-ptn](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.ptn.batch.batch-account-ptn.yml/badge.svg)](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.ptn.batch.batch-account-ptn.yml) (test ptn) | | [![avm.res.key-vault.vault](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg)](https://github.com/eriqua/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) (test res) | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/workflows/avm.template.module.yml | 15 +++++++--- .../compliance/module.tests.ps1 | 29 ++++++++++--------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/.github/workflows/avm.template.module.yml b/.github/workflows/avm.template.module.yml index 5d8ea22141..70eeb5ea5b 100644 --- a/.github/workflows/avm.template.module.yml +++ b/.github/workflows/avm.template.module.yml @@ -52,7 +52,7 @@ jobs: job_psrule_test: # Note: Please don't change this job name. It is used by the setEnvironment action to define which PS modules to install on runners. name: "PSRule [${{ matrix.testCases.name }}]" runs-on: ubuntu-latest - if: (fromJson(inputs.workflowInput)).staticValidation == 'true' + if: ${{ inputs.psRuleModuleTestFilePaths != '' && (fromJson(inputs.workflowInput)).staticValidation == 'true' }} strategy: fail-fast: false matrix: @@ -74,7 +74,7 @@ jobs: job_psrule_test_waf_reliability: # Note: Please don't change this job name. It is used by the setEnvironment action to define which PS modules to install on runners. name: "PSRule - WAF Reliability [${{ matrix.testCases.name }}]" runs-on: ubuntu-latest - if: (fromJson(inputs.workflowInput)).staticValidation == 'true' + if: ${{ inputs.psRuleModuleTestFilePaths != '' && (fromJson(inputs.workflowInput)).staticValidation == 'true' }} strategy: fail-fast: false matrix: @@ -101,7 +101,9 @@ jobs: runs-on: ubuntu-latest if: | !cancelled() && - (fromJson(inputs.workflowInput)).deploymentValidation == 'true' && needs.job_module_static_validation.result != 'failure' && needs.job_psrule_test_waf_reliability.result != 'failure' + (fromJson(inputs.workflowInput)).deploymentValidation == 'true' && + needs.job_module_static_validation.result != 'failure' && + needs.job_psrule_test_waf_reliability.result != 'failure' needs: - job_module_static_validation - job_psrule_test_waf_reliability @@ -136,8 +138,13 @@ jobs: job_publish_module: # Note: Please don't change this job name. It is used by the setEnvironment action to define which PS modules to install on runners. name: "Publishing" runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' && success() + # Note: Below always() required in condition due to psrule jobs being skipped for ptn modules not having defaults or waf-aligned folders + if: github.ref == 'refs/heads/main' && + always() && + needs.job_module_static_validation.result == 'success' && + needs.job_module_deploy_validation.result == 'success' needs: + - job_module_static_validation - job_module_deploy_validation steps: - name: "Checkout" diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 index 91edf4a47f..74bf8331c5 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 @@ -59,7 +59,7 @@ Describe 'File/folder tests' -Tag 'Modules' { $moduleFolderTestCases = [System.Collections.ArrayList] @() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $moduleFolderTestCases += @{ moduleFolderName = $resourceTypeIdentifier moduleFolderPath = $moduleFolderPath @@ -146,11 +146,13 @@ Describe 'File/folder tests' -Tag 'Modules' { $topLevelModuleTestCases = [System.Collections.ArrayList]@() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $moduleTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[1] # 'avm/res|ptn//' would return 'res|ptn' if (($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2) { $topLevelModuleTestCases += @{ - moduleFolderName = $moduleFolderPath.Replace('\', '/').Split('/avm/')[1] - moduleFolderPath = $moduleFolderPath + moduleFolderName = $moduleFolderPath.Replace('\', '/').Split('/avm/')[1] + moduleFolderPath = $moduleFolderPath + moduleTypeIdentifier = $moduleTypeIdentifier } } } @@ -185,7 +187,7 @@ Describe 'File/folder tests' -Tag 'Modules' { $pathExisting | Should -Be $true } - It '[] Module should contain a [` tests/e2e/*waf-aligned `] folder.' -TestCases $topLevelModuleTestCases { + It '[] Module should contain a [` tests/e2e/*waf-aligned `] folder.' -TestCases ($topLevelModuleTestCases | Where-Object { $_.moduleTypeIdentifier -eq 'res' }) { param( [string] $moduleFolderPath @@ -195,7 +197,7 @@ Describe 'File/folder tests' -Tag 'Modules' { $wafAlignedFolder | Should -Not -BeNullOrEmpty } - It '[] Module should contain a [` tests/e2e/*defaults `] folder.' -TestCases $topLevelModuleTestCases { + It '[] Module should contain a [` tests/e2e/*defaults `] folder.' -TestCases ($topLevelModuleTestCases | Where-Object { $_.moduleTypeIdentifier -eq 'res' }) { param( [string] $moduleFolderPath @@ -227,7 +229,7 @@ Describe 'Pipeline tests' -Tag 'Pipeline' { $pipelineTestCases = [System.Collections.ArrayList] @() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $relativeModulePath = Join-Path 'avm' ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}')[1] $isTopLevelModule = ($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2 @@ -282,7 +284,7 @@ Describe 'Module tests' -Tag 'Module' { foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $templateFilePath = Join-Path $moduleFolderPath 'main.bicep' $readmeFileTestCases += @{ @@ -340,7 +342,7 @@ Describe 'Module tests' -Tag 'Module' { continue } - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $armTemplateTestCases += @{ moduleFolderName = $resourceTypeIdentifier @@ -392,7 +394,7 @@ Describe 'Module tests' -Tag 'Module' { $templateFilePath = Join-Path $moduleFolderPath 'main.bicep' $templateFileContent = $builtTestFileMap[$templateFilePath] - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' # Test file setup $moduleFolderTestCases += @{ @@ -618,7 +620,7 @@ Describe 'Module tests' -Tag 'Module' { $udtSpecificTestCases = [System.Collections.ArrayList] @() # Specific UDT test cases for singular UDTs (e.g. tags) foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $templateFilePath = Join-Path $moduleFolderPath 'main.bicep' $templateFileContent = $builtTestFileMap[$templateFilePath] @@ -1102,7 +1104,7 @@ Describe 'Governance tests' { $governanceTestCases = [System.Collections.ArrayList] @() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $relativeModulePath = Join-Path 'avm' ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}')[1] $isTopLevelModule = ($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2 @@ -1138,7 +1140,6 @@ Describe 'Governance tests' { $moduleLine | Should -Be $expectedEntry -Because 'the module should match the expected format as documented [here](https://azure.github.io/Azure-Verified-Modules/specs/shared/#codeowners-file).' } - It '[] Module identifier should be listed in issue template in the correct alphabetical position.' -TestCases $governanceTestCases { param( @@ -1194,7 +1195,7 @@ Describe 'Test file tests' -Tag 'TestTemplate' { $testFilePaths = (Get-ChildItem -Path $moduleFolderPath -Recurse -Filter 'main.test.bicep').FullName | Sort-Object foreach ($testFilePath in $testFilePaths) { $testFileContent = Get-Content $testFilePath - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # avm/res// + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' $deploymentTestFileTestCases += @{ testName = Split-Path (Split-Path $testFilePath) -Leaf From 81b31acdbf47416377942d2b67667d1212ddcb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 10 Apr 2024 09:58:59 +0200 Subject: [PATCH 13/66] feat: `avm/res/aad/domain-service` (#1457) ## Description Adds Azure Active Directory Domain Services module. Additional changes: - The module requires changes to the testing process, as only one instance can be provisioned in a tenant. - The script generating certificates for testing has been fixed. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.aad.domain-service](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml/badge.svg?branch=aad-domain-services)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml) | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../workflows/avm.res.aad.domain-service.yml | 85 ++ avm/res/aad/domain-service/README.md | 805 ++++++++++++++++++ avm/res/aad/domain-service/main.bicep | 399 +++++++++ avm/res/aad/domain-service/main.json | 601 +++++++++++++ .../tests/e2e/waf-aligned/dependencies.bicep | 172 ++++ .../tests/e2e/waf-aligned/main.test.bicep | 188 ++++ .../tests/e2e/waf-aligned/main.wait.bicep | 27 + avm/res/aad/domain-service/version.json | 7 + .../e2e-template-assets/scripts/.gitignore | 4 + .../scripts/Set-PfxCertificateInKeyVault.ps1 | 38 +- .../compliance/module.tests.ps1 | 6 + 13 files changed, 2320 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/avm.res.aad.domain-service.yml create mode 100644 avm/res/aad/domain-service/README.md create mode 100644 avm/res/aad/domain-service/main.bicep create mode 100644 avm/res/aad/domain-service/main.json create mode 100644 avm/res/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/aad/domain-service/tests/e2e/waf-aligned/main.wait.bicep create mode 100644 avm/res/aad/domain-service/version.json create mode 100644 avm/utilities/e2e-template-assets/scripts/.gitignore diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 81832c9bba..da03f81c8a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,7 @@ /scripts/ @Azure/bicep-admins @Azure/avm-core-team-technical-bicep /avm/ @Azure/avm-core-team-technical-bicep /avm/utilities/ @Azure/avm-core-team-technical-bicep -#/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep +/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/api-management/service/ @Azure/avm-res-apimanagement-service-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/app/container-app/ @Azure/avm-res-app-containerapp-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 6696605d92..cd6d3f20ce 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -43,7 +43,7 @@ body: # - "avm/ptn/avd-lza/networking" # - "avm/ptn/avd-lza/session-hosts" # - "avm/ptn/security/security-center" - # - "avm/res/aad/domain-service" + - "avm/res/aad/domain-service" - "avm/res/analysis-services/server" - "avm/res/api-management/service" - "avm/res/app-configuration/configuration-store" diff --git a/.github/workflows/avm.res.aad.domain-service.yml b/.github/workflows/avm.res.aad.domain-service.yml new file mode 100644 index 0000000000..b5eb8d5e6a --- /dev/null +++ b/.github/workflows/avm.res.aad.domain-service.yml @@ -0,0 +1,85 @@ +name: "avm.res.aad.domain-service" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.aad.domain-service.yml" + - "avm/res/aad/domain-service/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/aad/domain-service" + workflowPath: ".github/workflows/avm.res.aad.domain-service.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md new file mode 100644 index 0000000000..ef5487d2e5 --- /dev/null +++ b/avm/res/aad/domain-service/README.md @@ -0,0 +1,805 @@ +# Azure Active Directory Domain Services `[Microsoft.AAD/domainServices]` + +This module deploys an Azure Active Directory Domain Services (AADDS). + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Notes](#Notes) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.AAD/domainServices` | [2022-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.AAD/2022-12-01/domainServices) | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/aad/domain-service:`. + +- [WAF-aligned](#example-1-waf-aligned) + +### Example 1: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +
+ +via Bicep module + +```bicep +module domainService 'br/public:avm/res/aad/domain-service:' = { + name: 'domainServiceDeployment' + params: { + // Required parameters + domainName: 'onmicrosoft.com' + // Non-required parameters + additionalRecipients: [ + '@noreply.github.com' + ] + diagnosticSettings: '' + enableTelemetry: '' + externalAccess: 'Enabled' + ldaps: 'Enabled' + location: '' + lock: '' + name: 'aaddswaf001' + pfxCertificate: '' + pfxCertificatePassword: '' + replicaSets: '' + sku: 'Standard' + tags: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "domainName": { + "value": "onmicrosoft.com" + }, + // Non-required parameters + "additionalRecipients": { + "value": [ + "@noreply.github.com" + ] + }, + "diagnosticSettings": { + "value": "" + }, + "enableTelemetry": { + "value": "" + }, + "externalAccess": { + "value": "Enabled" + }, + "ldaps": { + "value": "Enabled" + }, + "location": { + "value": "" + }, + "lock": { + "value": "" + }, + "name": { + "value": "aaddswaf001" + }, + "pfxCertificate": { + "value": "" + }, + "pfxCertificatePassword": { + "value": "" + }, + "replicaSets": { + "value": "" + }, + "sku": { + "value": "Standard" + }, + "tags": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`domainName`](#parameter-domainname) | string | The domain name specific to the Azure ADDS service. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`pfxCertificate`](#parameter-pfxcertificate) | securestring | The certificate required to configure Secure LDAP. Should be a base64encoded representation of the certificate PFX file and contain the domainName as CN. Required if secure LDAP is enabled and must be valid more than 30 days. | +| [`pfxCertificatePassword`](#parameter-pfxcertificatepassword) | securestring | The password to decrypt the provided Secure LDAP certificate PFX file. Required if secure LDAP is enabled. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`additionalRecipients`](#parameter-additionalrecipients) | array | The email recipient value to receive alerts. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`domainConfigurationType`](#parameter-domainconfigurationtype) | string | The value is to provide domain configuration type. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`externalAccess`](#parameter-externalaccess) | string | The value is to enable the Secure LDAP for external services of Azure ADDS Services. | +| [`filteredSync`](#parameter-filteredsync) | string | The value is to synchronize scoped users and groups. | +| [`kerberosArmoring`](#parameter-kerberosarmoring) | string | The value is to enable to provide a protected channel between the Kerberos client and the KDC. | +| [`kerberosRc4Encryption`](#parameter-kerberosrc4encryption) | string | The value is to enable Kerberos requests that use RC4 encryption. | +| [`ldaps`](#parameter-ldaps) | string | A flag to determine whether or not Secure LDAP is enabled or disabled. | +| [`location`](#parameter-location) | string | The location to deploy the Azure ADDS Services. Uses the resource group location if not specified. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`name`](#parameter-name) | string | The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service. The prefix of your specified domain name (such as dscontoso in the dscontoso.com domain name) must contain 15 or fewer characters. | +| [`notifyDcAdmins`](#parameter-notifydcadmins) | string | The value is to notify the DC Admins. | +| [`notifyGlobalAdmins`](#parameter-notifyglobaladmins) | string | The value is to notify the Global Admins. | +| [`ntlmV1`](#parameter-ntlmv1) | string | The value is to enable clients making request using NTLM v1. | +| [`replicaSets`](#parameter-replicasets) | array | Additional replica set for the managed domain. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`sku`](#parameter-sku) | string | The name of the SKU specific to Azure ADDS Services. | +| [`syncNtlmPasswords`](#parameter-syncntlmpasswords) | string | The value is to enable synchronized users to use NTLM authentication. | +| [`syncOnPremPasswords`](#parameter-synconprempasswords) | string | The value is to enable on-premises users to authenticate against managed domain. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`tlsV1`](#parameter-tlsv1) | string | The value is to enable clients making request using TLSv1. | + +### Parameter: `domainName` + +The domain name specific to the Azure ADDS service. + +- Required: Yes +- Type: string + +### Parameter: `pfxCertificate` + +The certificate required to configure Secure LDAP. Should be a base64encoded representation of the certificate PFX file and contain the domainName as CN. Required if secure LDAP is enabled and must be valid more than 30 days. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `pfxCertificatePassword` + +The password to decrypt the provided Secure LDAP certificate PFX file. Required if secure LDAP is enabled. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `additionalRecipients` + +The email recipient value to receive alerts. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `domainConfigurationType` + +The value is to provide domain configuration type. + +- Required: No +- Type: string +- Default: `'FullySynced'` +- Allowed: + ```Bicep + [ + 'FullySynced' + 'ResourceTrusting' + ] + ``` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `externalAccess` + +The value is to enable the Secure LDAP for external services of Azure ADDS Services. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `filteredSync` + +The value is to synchronize scoped users and groups. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `kerberosArmoring` + +The value is to enable to provide a protected channel between the Kerberos client and the KDC. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `kerberosRc4Encryption` + +The value is to enable Kerberos requests that use RC4 encryption. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `ldaps` + +A flag to determine whether or not Secure LDAP is enabled or disabled. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `location` + +The location to deploy the Azure ADDS Services. Uses the resource group location if not specified. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `name` + +The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service. The prefix of your specified domain name (such as dscontoso in the dscontoso.com domain name) must contain 15 or fewer characters. + +- Required: No +- Type: string +- Default: `[parameters('domainName')]` + +### Parameter: `notifyDcAdmins` + +The value is to notify the DC Admins. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `notifyGlobalAdmins` + +The value is to notify the Global Admins. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `ntlmV1` + +The value is to enable clients making request using NTLM v1. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `replicaSets` + +Additional replica set for the managed domain. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`location`](#parameter-replicasetslocation) | string | Virtual network location. | +| [`subnetId`](#parameter-replicasetssubnetid) | string | The id of the subnet that Domain Services will be deployed on. The subnet has some requirements, which are outlined in the [notes section](#Network-Security-Group-NSG-requirements-for-AADDS) of the documentation. | + +### Parameter: `replicaSets.location` + +Virtual network location. + +- Required: Yes +- Type: string + +### Parameter: `replicaSets.subnetId` + +The id of the subnet that Domain Services will be deployed on. The subnet has some requirements, which are outlined in the [notes section](#Network-Security-Group-NSG-requirements-for-AADDS) of the documentation. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `sku` + +The name of the SKU specific to Azure ADDS Services. + +- Required: No +- Type: string +- Default: `'Standard'` +- Allowed: + ```Bicep + [ + 'Enterprise' + 'Premium' + 'Standard' + ] + ``` + +### Parameter: `syncNtlmPasswords` + +The value is to enable synchronized users to use NTLM authentication. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `syncOnPremPasswords` + +The value is to enable on-premises users to authenticate against managed domain. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `tlsV1` + +The value is to enable clients making request using TLSv1. + +- Required: No +- Type: string +- Default: `'Enabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The domain name of the Azure Active Directory Domain Services(Azure ADDS). | +| `resourceGroupName` | string | The name of the resource group the Azure Active Directory Domain Services(Azure ADDS) was created in. | +| `resourceId` | string | The resource ID of the Azure Active Directory Domain Services(Azure ADDS). | + +## Cross-referenced modules + +_None_ + +## Notes + +This module requires prerequisites, that can't be done via ARM/Bicep at the moment. The prerequisites to create a managed domain are outlined [here](https://learn.microsoft.com/en-us/entra/identity/domain-services/template-create-instance#prerequisites). + +>**Note**: Please make sure you follow the steps to make sure the prerequisites are fullfilled before using the module. + +### Create a Service Principal + +Follow the steps [Create required Microsoft Entra resources](https://learn.microsoft.com/en-us/entra/identity/domain-services/template-create-instance#create-required-microsoft-entra-resources), which are summarized in the following steps: + +1. Prepare PowerShell for Graph interaction + - [Install the Microsoft Graph PowerShell SDK](https://learn.microsoft.com/en-us/powershell/microsoftgraph/installation?view=graph-powershell-1.0) ```Install-Module Microsoft.Graph -Scope CurrentUser``` + - Import the needed module ```Import-Module -Name Microsoft.Graph.Identity.Governance -Force``` + +1. [Assign](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/manage-roles-portal) the [Application Developer](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#application-developer) to the current user to add the required Service Principal. + + ```powershell + # Connect (will open a browser) + Connect-MgGraph -Scopes "User.Read.All,Application.ReadWrite.All" + # Replace with your user + $user = Get-MgUser -Filter "userPrincipalName eq 'johndoe@contoso.com'" + # Get the role to assign it to the user + $roledefinition = Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq 'Application Developer'" + # Assign the role (without PIM) + $roleassignment = New-MgRoleManagementDirectoryRoleAssignment -DirectoryScopeId '/' -RoleDefinitionId $roledefinition.Id -PrincipalId $user.Id + ``` + + If you have PIM activated, assign the role like this: + + ```powershell + # limit the assignment to 1 hour + $params = @{ + "PrincipalId" = $user.Id + "RoleDefinitionId" = $roledefinition.Id + "Justification" = "Add eligible assignment" + "DirectoryScopeId" = "/" + "Action" = "AdminAssign" + "ScheduleInfo" = @{ + "StartDateTime" = Get-Date + "Expiration" = @{ + "Type" = "AfterDuration" + "Duration" = "PT1H" + } + } + } + New-MgRoleManagementDirectoryRoleEligibilityScheduleRequest -BodyParameter $params | Format-List Id, Status, Action, AppScopeId, DirectoryScopeId, RoleDefinitionId, IsValidationOnly, Justification, PrincipalId, CompletedDateTime, CreatedDateTime + ``` + +3. Create the necessary Service Principal + + ```powershell + New-MgServicePrincipal -AppId 2565bd9d-da50-47d4-8b85-4c97f669dc36 -DisplayName "Domain Controller Services" + ``` + +#### GitHub Action deployment testing + +In order to provision Entra Domain Services, the Service Principal that has been set up in [Setup your Azure test environment](https://azure.github.io/Azure-Verified-Modules/contributing/bicep/bicep-contribution-flow/#1-setup-your-azure-test-environment) needs two additional roles, as of [Tutorial: Create and configure a Microsoft Entra Domain Services managed domain - Prerequisites](https://learn.microsoft.com/en-us/entra/identity/domain-services/tutorial-create-instance#prerequisites). + +### Network Security Group (NSG) requirements for AADDS + +- A network security group has to be created and assigned to the designated AADDS subnet before deploying this module + - The following inbound rules should be allowed on the network security group + | Name | Protocol | Source Port Range | Source Address Prefix | Destination Port Range | Destination Address Prefix | + | - | - | - | - | - | - | + | AllowSyncWithAzureAD | TCP | `*` | `AzureActiveDirectoryDomainServices` | `443` | `*` | + | AllowPSRemoting | TCP | `*` | `AzureActiveDirectoryDomainServices` | `5986` | `*` | + | AllowLDAPs | TCP | `*` | `VirtualNetwork` | `5986` | `*` | +- Associating a route table to the AADDS subnet is not recommended +- The network used for AADDS must have its [DNS Servers configured](https://learn.microsoft.com/en-us/azure/active-directory-domain-services/tutorial-configure-networking#configure-dns-servers-in-the-peered-virtual-network) (e.g. with IPs `10.0.1.4` & `10.0.1.5`) + +### Create self-signed certificate for secure LDAP +Follow the below PowerShell commands to get base64 encoded string of a self-signed certificate (with a `pfxCertificatePassword`) + +```PowerShell +$pfxCertificatePassword = ConvertTo-SecureString '[[YourPfxCertificatePassword]]' -AsPlainText -Force +$certInputObject = @{ + Subject = 'CN=*.[[YourDomainName]]' + DnsName = '*.[[YourDomainName]]' + CertStoreLocation = 'cert:\LocalMachine\My' + KeyExportPolicy = 'Exportable' + Provider = 'Microsoft Enhanced RSA and AES Cryptographic Provider' + NotAfter = (Get-Date).AddMonths(3) + HashAlgorithm = 'SHA256' +} +$rawCert = New-SelfSignedCertificate @certInputObject +Export-PfxCertificate -Cert ('Cert:\localmachine\my\' + $rawCert.Thumbprint) -FilePath "$home/aadds.pfx" -Password $pfxCertificatePassword -Force +$rawCertByteStream = Get-Content "$home/aadds.pfx" -AsByteStream +$pfxCertificate = [System.Convert]::ToBase64String($rawCertByteStream) +``` + +### References + +- [Prerequisites to use PowerShell or Graph Explorer for Microsoft Entra roles](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/prerequisites) +- [Assign Microsoft Entra roles to users](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/manage-roles-portal) +- [New-MgServicePrincipal](https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.applications/new-mgserviceprincipal) +- [Create a Microsoft Entra Domain Services managed domain using an Azure Resource Manager template](https://learn.microsoft.com/en-us/entra/identity/domain-services/template-create-instance) + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/aad/domain-service/main.bicep b/avm/res/aad/domain-service/main.bicep new file mode 100644 index 0000000000..1e178c17fd --- /dev/null +++ b/avm/res/aad/domain-service/main.bicep @@ -0,0 +1,399 @@ +metadata name = 'Azure Active Directory Domain Services' +metadata description = 'This module deploys an Azure Active Directory Domain Services (AADDS).' +metadata owner = 'Azure/module-maintainers' + +@minLength(1) +@maxLength(19) // 15 characters for domain name + 4 characters for suffix +@description('Optional. The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service. The prefix of your specified domain name (such as dscontoso in the dscontoso.com domain name) must contain 15 or fewer characters.') +param name string = domainName + +@description('Optional. The location to deploy the Azure ADDS Services. Uses the resource group location if not specified.') +param location string = resourceGroup().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +// ============ // +// Parameters // +// ============ // + +@minLength(1) +@metadata({ + example: ''' + - 'contoso.onmicrosoft.com' + - 'aaddscontoso.com' + ''' +}) +@description('Required. The domain name specific to the Azure ADDS service.') +param domainName string + +@description('Optional. The name of the SKU specific to Azure ADDS Services.') +@allowed([ + 'Standard' + 'Enterprise' + 'Premium' +]) +param sku string = 'Standard' + +@description('Optional. Additional replica set for the managed domain.') +param replicaSets replicaSetType + +@description('Conditional. The certificate required to configure Secure LDAP. Should be a base64encoded representation of the certificate PFX file and contain the domainName as CN. Required if secure LDAP is enabled and must be valid more than 30 days.') +@secure() +param pfxCertificate string = '' + +@description('Conditional. The password to decrypt the provided Secure LDAP certificate PFX file. Required if secure LDAP is enabled.') +@secure() +param pfxCertificatePassword string = '' + +@metadata({ + example: ''' + - ['john@doh.org'] + - ['john@doh.org','jane@doh.org'] + ''' +}) +@description('Optional. The email recipient value to receive alerts.') +param additionalRecipients array = [] + +@description('Optional. The value is to provide domain configuration type.') +@allowed([ + 'FullySynced' + 'ResourceTrusting' +]) +param domainConfigurationType string = 'FullySynced' + +@description('Optional. The value is to synchronize scoped users and groups.') +@allowed([ + 'Disabled' + 'Enabled' +]) +param filteredSync string = 'Enabled' + +@description('Optional. The value is to enable clients making request using TLSv1.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param tlsV1 string = 'Enabled' + +@description('Optional. The value is to enable clients making request using NTLM v1.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param ntlmV1 string = 'Enabled' + +@description('Optional. The value is to enable synchronized users to use NTLM authentication.') +@allowed([ + 'Enabled' + 'Disabled' +]) +#disable-next-line secure-secrets-in-params // Not a secret +param syncNtlmPasswords string = 'Enabled' + +@description('Optional. The value is to enable on-premises users to authenticate against managed domain.') +@allowed([ + 'Enabled' + 'Disabled' +]) +#disable-next-line secure-secrets-in-params // Not a secret +param syncOnPremPasswords string = 'Enabled' + +@description('Optional. The value is to enable Kerberos requests that use RC4 encryption.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param kerberosRc4Encryption string = 'Enabled' + +@description('Optional. The value is to enable to provide a protected channel between the Kerberos client and the KDC.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param kerberosArmoring string = 'Enabled' + +@description('Optional. The value is to notify the DC Admins.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param notifyDcAdmins string = 'Enabled' + +@description('Optional. The value is to notify the Global Admins.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param notifyGlobalAdmins string = 'Enabled' + +@description('Optional. The value is to enable the Secure LDAP for external services of Azure ADDS Services.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param externalAccess string = 'Enabled' + +@description('Optional. A flag to determine whether or not Secure LDAP is enabled or disabled.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param ldaps string = 'Enabled' + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@metadata({ + example: ''' + { + "key1": "value1", + "key2": "value2" + } + ''' +}) +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Generated. Built-in roles "General": https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#general.') +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +// ============== // +// Resources // +// ============== // + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.aad-domainservice.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +resource domainservice 'Microsoft.AAD/domainServices@2022-12-01' = { + name: name + location: location + tags: tags + properties: { + domainName: domainName + domainConfigurationType: domainConfigurationType + filteredSync: filteredSync + notificationSettings: { + additionalRecipients: additionalRecipients + notifyDcAdmins: notifyDcAdmins + notifyGlobalAdmins: notifyGlobalAdmins + } + ldapsSettings: { + externalAccess: externalAccess + ldaps: ldaps + pfxCertificate: !empty(pfxCertificate) ? pfxCertificate : null + pfxCertificatePassword: !empty(pfxCertificatePassword) ? pfxCertificatePassword : null + } + replicaSets: replicaSets + domainSecuritySettings: { + tlsV1: tlsV1 + ntlmV1: ntlmV1 + syncNtlmPasswords: syncNtlmPasswords + syncOnPremPasswords: syncOnPremPasswords + kerberosRc4Encryption: kerberosRc4Encryption + kerberosArmoring: kerberosArmoring + } + sku: sku + } +} + +resource domainservice_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: domainservice + } +] + +resource domainservice_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: domainservice + } + +resource domainservice_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(domainservice.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: domainservice + } +] + +// ============ // +// Outputs // +// ============ // + +@description('The domain name of the Azure Active Directory Domain Services(Azure ADDS).') +output name string = domainservice.name + +@description('The name of the resource group the Azure Active Directory Domain Services(Azure ADDS) was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Azure Active Directory Domain Services(Azure ADDS).') +output resourceId string = domainservice.id + +@description('The location the resource was deployed into.') +output location string = domainservice.location + +// ================ // +// Definitions // +// ================ // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @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? + + @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? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? + +type replicaSetType = { + @description('Required. Virtual network location.') + location: string + + @metadata({ + example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/' + }) + @description('Required. The id of the subnet that Domain Services will be deployed on. The subnet has some requirements, which are outlined in the [notes section](#Network-Security-Group-NSG-requirements-for-AADDS) of the documentation.') + subnetId: string +}[]? diff --git a/avm/res/aad/domain-service/main.json b/avm/res/aad/domain-service/main.json new file mode 100644 index 0000000000..d5362f9fcb --- /dev/null +++ b/avm/res/aad/domain-service/main.json @@ -0,0 +1,601 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "10981571743323253420" + }, + "name": "Azure Active Directory Domain Services", + "description": "This module deploys an Azure Active Directory Domain Services (AADDS).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "replicaSetType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "location": { + "type": "string", + "metadata": { + "description": "Required. Virtual network location." + } + }, + "subnetId": { + "type": "string", + "metadata": { + "example": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/", + "description": "Required. The id of the subnet that Domain Services will be deployed on. The subnet has some requirements, which are outlined in the [notes section](#Network-Security-Group-NSG-requirements-for-AADDS) of the documentation." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[parameters('domainName')]", + "minLength": 1, + "maxLength": 19, + "metadata": { + "description": "Optional. The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service. The prefix of your specified domain name (such as dscontoso in the dscontoso.com domain name) must contain 15 or fewer characters." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location to deploy the Azure ADDS Services. Uses the resource group location if not specified." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "domainName": { + "type": "string", + "metadata": { + "example": " - 'contoso.onmicrosoft.com'\n - 'aaddscontoso.com'\n ", + "description": "Required. The domain name specific to the Azure ADDS service." + }, + "minLength": 1 + }, + "sku": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard", + "Enterprise", + "Premium" + ], + "metadata": { + "description": "Optional. The name of the SKU specific to Azure ADDS Services." + } + }, + "replicaSets": { + "$ref": "#/definitions/replicaSetType", + "metadata": { + "description": "Optional. Additional replica set for the managed domain." + } + }, + "pfxCertificate": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. The certificate required to configure Secure LDAP. Should be a base64encoded representation of the certificate PFX file and contain the domainName as CN. Required if secure LDAP is enabled and must be valid more than 30 days." + } + }, + "pfxCertificatePassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. The password to decrypt the provided Secure LDAP certificate PFX file. Required if secure LDAP is enabled." + } + }, + "additionalRecipients": { + "type": "array", + "defaultValue": [], + "metadata": { + "example": " - ['john@doh.org']\n - ['john@doh.org','jane@doh.org']\n ", + "description": "Optional. The email recipient value to receive alerts." + } + }, + "domainConfigurationType": { + "type": "string", + "defaultValue": "FullySynced", + "allowedValues": [ + "FullySynced", + "ResourceTrusting" + ], + "metadata": { + "description": "Optional. The value is to provide domain configuration type." + } + }, + "filteredSync": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. The value is to synchronize scoped users and groups." + } + }, + "tlsV1": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable clients making request using TLSv1." + } + }, + "ntlmV1": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable clients making request using NTLM v1." + } + }, + "syncNtlmPasswords": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable synchronized users to use NTLM authentication." + } + }, + "syncOnPremPasswords": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable on-premises users to authenticate against managed domain." + } + }, + "kerberosRc4Encryption": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable Kerberos requests that use RC4 encryption." + } + }, + "kerberosArmoring": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable to provide a protected channel between the Kerberos client and the KDC." + } + }, + "notifyDcAdmins": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to notify the DC Admins." + } + }, + "notifyGlobalAdmins": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to notify the Global Admins." + } + }, + "externalAccess": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The value is to enable the Secure LDAP for external services of Azure ADDS Services." + } + }, + "ldaps": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. A flag to determine whether or not Secure LDAP is enabled or disabled." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "example": " {\n \"key1\": \"value1\",\n \"key2\": \"value2\"\n }\n ", + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.aad-domainservice.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "domainservice": { + "type": "Microsoft.AAD/domainServices", + "apiVersion": "2022-12-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "domainName": "[parameters('domainName')]", + "domainConfigurationType": "[parameters('domainConfigurationType')]", + "filteredSync": "[parameters('filteredSync')]", + "notificationSettings": { + "additionalRecipients": "[parameters('additionalRecipients')]", + "notifyDcAdmins": "[parameters('notifyDcAdmins')]", + "notifyGlobalAdmins": "[parameters('notifyGlobalAdmins')]" + }, + "ldapsSettings": { + "externalAccess": "[parameters('externalAccess')]", + "ldaps": "[parameters('ldaps')]", + "pfxCertificate": "[if(not(empty(parameters('pfxCertificate'))), parameters('pfxCertificate'), null())]", + "pfxCertificatePassword": "[if(not(empty(parameters('pfxCertificatePassword'))), parameters('pfxCertificatePassword'), null())]" + }, + "replicaSets": "[parameters('replicaSets')]", + "domainSecuritySettings": { + "tlsV1": "[parameters('tlsV1')]", + "ntlmV1": "[parameters('ntlmV1')]", + "syncNtlmPasswords": "[parameters('syncNtlmPasswords')]", + "syncOnPremPasswords": "[parameters('syncOnPremPasswords')]", + "kerberosRc4Encryption": "[parameters('kerberosRc4Encryption')]", + "kerberosArmoring": "[parameters('kerberosArmoring')]" + }, + "sku": "[parameters('sku')]" + } + }, + "domainservice_diagnosticSettings": { + "copy": { + "name": "domainservice_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.AAD/domainServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'AllLogs', 'enabled', true())))]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "domainservice" + ] + }, + "domainservice_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.AAD/domainServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "domainservice" + ] + }, + "domainservice_roleAssignments": { + "copy": { + "name": "domainservice_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.AAD/domainServices/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.AAD/domainServices', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "domainservice" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The domain name of the Azure Active Directory Domain Services(Azure ADDS)." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Azure Active Directory Domain Services(Azure ADDS) was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Active Directory Domain Services(Azure ADDS)." + }, + "value": "[resourceId('Microsoft.AAD/domainServices', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('domainservice', '2022-12-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..c8b8141b55 --- /dev/null +++ b/avm/res/aad/domain-service/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,172 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string + +@description('Required. The name of the Deployment Script to create for the Certificate generation.') +param certDeploymentScriptName string + +var certPWSecretName = 'pfxCertificatePassword' +var certSecretName = 'pfxBase64Certificate' +var addressPrefix = '10.0.0.0/16' +var aadsSubnetAddressPrefix = cidrSubnet(addressPrefix, 24, 1) + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + dhcpOptions: { + // set the DNS servers to the 4th and 5th addresses in the subnet + dnsServers: [for i in range(3, 2): cidrHost(aadsSubnetAddressPrefix, i)] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + } + } + { + name: 'aadds-subnet' + properties: { + addressPrefix: aadsSubnetAddressPrefix + networkSecurityGroup: { + id: nsgAaddSubnet.id + } + } + } + ] + } +} + +resource nsgAaddSubnet 'Microsoft.Network/networkSecurityGroups@2023-09-01' = { + name: '${virtualNetworkName}-aadds-subnet-nsg' + location: location + properties: { + securityRules: [ + { + name: 'AllowSyncWithAzureAD' + properties: { + access: 'Allow' + priority: 101 + direction: 'Inbound' + protocol: 'Tcp' + sourceAddressPrefix: 'AzureActiveDirectoryDomainServices' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '443' + } + } + { + name: 'AllowPSRemoting' + properties: { + access: 'Allow' + priority: 301 + direction: 'Inbound' + protocol: 'Tcp' + sourceAddressPrefix: 'AzureActiveDirectoryDomainServices' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '5986' + } + } + { + name: 'AllowLDAPs' + properties: { + access: 'Allow' + priority: 401 + direction: 'Inbound' + protocol: 'Tcp' + sourceAddressPrefix: 'VirtualNetwork' + sourcePortRange: '*' + destinationAddressPrefix: '*' + destinationPortRange: '636' + } + } + ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '00482a5a-887f-4fb3-b363-3b7fe8e74483' + ) // Key Vault Administrator + principalType: 'ServicePrincipal' + } +} + +resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: certDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '11.0' + retentionInterval: 'P1D' + arguments: ' -KeyVaultName "${keyVault.name}" -ResourceGroupName "${resourceGroup().name}" -NamePrefix "${namePrefix}" -CertPWSecretName "${certPWSecretName}" -CertSecretName "${certSecretName}"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-PfxCertificateInKeyVault.ps1') + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the certification password secret.') +output certPWSecretName string = certPWSecretName + +@description('The name of the certification secret.') +output certSecretName string = certSecretName + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..e4af67fbc9 --- /dev/null +++ b/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,188 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-aad-domainservices-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aaddswaf' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + namePrefix: namePrefix + certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: last(split(nestedDependencies.outputs.keyVaultResourceId, '/')) + scope: resourceGroup +} + +// 'idem' as second iteration will fail, as AAD DS is not ready for a second deployment during its provisioning state even when reported as 'succeeded' by the init iteration +// as of https://azure.github.io/Azure-Verified-Modules/specs/shared/#id-snfr7---category-testing---idempotency-tests the idem test it is not required +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-init' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + domainName: '${namePrefix}.onmicrosoft.com' + additionalRecipients: ['${namePrefix}@noreply.github.com'] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + logCategoriesAndGroups: [ + { + category: 'AllLogs' + } + ] + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + } + ] + lock: { + kind: 'None' + name: 'myCustomLockName' + } + ldaps: 'Enabled' + externalAccess: 'Enabled' + pfxCertificate: keyVault.getSecret(nestedDependencies.outputs.certSecretName) + pfxCertificatePassword: keyVault.getSecret(nestedDependencies.outputs.certPWSecretName) + replicaSets: [ + { + location: 'NorthEurope' + subnetId: nestedDependencies.outputs.subnetResourceId + } + ] + sku: 'Standard' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} + +module waitForDeployment 'main.wait.bicep' = { + dependsOn: [testDeployment] + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-waitForDeployment' + params: { + serviceShort: serviceShort + resourceLocation: resourceLocation + waitTimeInSeconds: '3000' // 50 min + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} + +// copy from the init test. Will be executed after a wait time +module testDeploymentIdem '../../../main.bicep' = { + dependsOn: [waitForDeployment] + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-idem' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + domainName: '${namePrefix}.onmicrosoft.com' + additionalRecipients: ['${namePrefix}@noreply.github.com'] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + logCategoriesAndGroups: [ + { + category: 'AllLogs' + } + ] + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + } + ] + lock: { + kind: 'None' + name: 'myCustomLockName' + } + ldaps: 'Enabled' + externalAccess: 'Enabled' + pfxCertificate: keyVault.getSecret(nestedDependencies.outputs.certSecretName) + pfxCertificatePassword: keyVault.getSecret(nestedDependencies.outputs.certPWSecretName) + replicaSets: [ + { + location: 'NorthEurope' + subnetId: nestedDependencies.outputs.subnetResourceId + } + ] + sku: 'Standard' + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.wait.bicep b/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.wait.bicep new file mode 100644 index 0000000000..409cff7b14 --- /dev/null +++ b/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.wait.bicep @@ -0,0 +1,27 @@ +@description('Optional. The location to deploy to.') +param resourceLocation string = resourceGroup().location + +param serviceShort string + +param waitTimeInSeconds string + +param tags object + +resource deployDelay 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-wait-${serviceShort}' + location: resourceLocation + kind: 'AzurePowerShell' + properties: { + retentionInterval: 'PT1H' + azPowerShellVersion: '11.0' + cleanupPreference: 'Always' + environmentVariables: [ + { + name: 'waitSeconds' + value: waitTimeInSeconds + } + ] + scriptContent: 'write-output "Sleeping for $Env:waitSeconds"; start-sleep -Seconds $Env:waitSeconds' + } + tags: tags +} diff --git a/avm/res/aad/domain-service/version.json b/avm/res/aad/domain-service/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/aad/domain-service/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/utilities/e2e-template-assets/scripts/.gitignore b/avm/utilities/e2e-template-assets/scripts/.gitignore new file mode 100644 index 0000000000..4b8dd751d1 --- /dev/null +++ b/avm/utilities/e2e-template-assets/scripts/.gitignore @@ -0,0 +1,4 @@ +; .gitignore file for certificates, that are created during testing +*.pfx +*.crt +*.key diff --git a/avm/utilities/e2e-template-assets/scripts/Set-PfxCertificateInKeyVault.ps1 b/avm/utilities/e2e-template-assets/scripts/Set-PfxCertificateInKeyVault.ps1 index fd88a2243e..26a6e5e95b 100644 --- a/avm/utilities/e2e-template-assets/scripts/Set-PfxCertificateInKeyVault.ps1 +++ b/avm/utilities/e2e-template-assets/scripts/Set-PfxCertificateInKeyVault.ps1 @@ -11,6 +11,9 @@ Mandatory. The name of the Key Vault to store the Certificate & Password in .PARAMETER ResourceGroupName Mandatory. The name of the Resource Group containing the Key Vault to store the Certificate & Password in +.PARAMETER NamePrefix +Mandatory. The name prefix to use for the certificate, which is used as prefix to .onmicrosoft.com to generate the CN for the certificate. + .PARAMETER CertPWSecretName Mandatory. The name of the Secret to store the Certificate's password in @@ -23,26 +26,33 @@ Mandatory. The name of the Secret to store the Secret in Generate a Certificate and store it as the Secret 'pfxCertificatePassword' in the Key Vault 'vault-rg' of Resource Group 'storage-rg' alongside its password as the Secret 'pfxCertificatePassword' #> param( - [Parameter(Mandatory = $true)] - [string] $KeyVaultName, + [Parameter(Mandatory = $true)] + [string] $KeyVaultName, + + [Parameter(Mandatory = $true)] + [string] $ResourceGroupName, - [Parameter(Mandatory = $true)] - [string] $ResourceGroupName, + [Parameter(Mandatory = $true)] + [string] $NamePrefix, - [Parameter(Mandatory = $true)] - [string] $CertPWSecretName, + [Parameter(Mandatory = $true)] + [string] $CertPWSecretName, - [Parameter(Mandatory = $true)] - [string] $CertSecretName + [Parameter(Mandatory = $true)] + [string] $CertSecretName ) -$password = ConvertTo-SecureString -String "$ResourceGroupName/$KeyVaultName/$CertSecretName" -AsPlainText -Force +$password = "$ResourceGroupName/$KeyVaultName/$CertSecretName" +$pfxPassword = ConvertTo-SecureString -String $password -AsPlainText -Force # Install open-ssl if not available apt-get install openssl # Generate certificate -openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout './privateKey.key' -out './certificate.crt' -subj '/CN=*.contoso.onmicrosoft.com/O=contoso/C=US' +$cn = '*.' + $namePrefix + '.onmicrosoft.com' +$subject = '/CN=' + $cn + '/O=contoso/C=US' +Write-Verbose ('Generating certificate for [{0}]' -f $cn) -Verbose +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout './privateKey.key' -out './certificate.crt' -subj $subject -addext 'extendedKeyUsage = serverAuth' # Sign certificate openssl pkcs12 -export -out 'aadds.pfx' -inkey './privateKey.key' -in './certificate.crt' -passout pass:$password @@ -54,9 +64,9 @@ $pfxCertificate = ConvertTo-SecureString -String ([System.Convert]::ToBase64Stri # Set values @( - @{ name = $CertPWSecretName; secretValue = $password } - @{ name = $CertSecretName; secretValue = $pfxCertificate } + @{ name = $CertPWSecretName; secretValue = $pfxPassword } + @{ name = $CertSecretName; secretValue = $pfxCertificate } ) | ForEach-Object { - $null = Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $_.name -SecretValue $_.secretValue - Write-Verbose ('Added secret [{0}] to key vault [{1}]' -f $_.name, $keyVaultName) -Verbose + $null = Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $_.name -SecretValue $_.secretValue + Write-Verbose ('Added secret [{0}] to key vault [{1}]' -f $_.name, $keyVaultName) -Verbose } diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 index 74bf8331c5..72bbe86ff0 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 @@ -203,6 +203,12 @@ Describe 'File/folder tests' -Tag 'Modules' { [string] $moduleFolderPath ) + # only one Domain-Services instance can be provisioned in a tenant and only one test (the waf-aligned) is possible. + if ($moduleFolderName.Equals('res/aad/domain-service')) { + Set-ItResult -Skipped -Because 'only one instance of the Domain-Service can be deployed at a time, and as such, also only one test can exist at a time.' + return + } + $defaultsFolder = Get-ChildItem -Directory (Join-Path -Path $moduleFolderPath 'tests' 'e2e') -Filter '*defaults' $defaultsFolder | Should -Not -BeNullOrEmpty } From 58f11a88bdfc5b805dbaec44d468990e9af8f6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 10 Apr 2024 11:53:15 +0200 Subject: [PATCH 14/66] fix: readme - avm/res/aad/domain-service (#1634) ## Description Fixes the readme by bringing it up to date ## Pipeline Reference ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/aad/domain-service/README.md | 130 ++++++++++++++++++++++---- avm/res/aad/domain-service/main.bicep | 2 +- avm/res/aad/domain-service/main.json | 61 +++++++++++- 3 files changed, 171 insertions(+), 22 deletions(-) diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index ef5487d2e5..640ab0847b 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -50,18 +50,47 @@ module domainService 'br/public:avm/res/aad/domain-service:' = { additionalRecipients: [ '@noreply.github.com' ] - diagnosticSettings: '' - enableTelemetry: '' + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + category: 'AllLogs' + } + ] + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] externalAccess: 'Enabled' ldaps: 'Enabled' location: '' - lock: '' + lock: { + kind: 'None' + name: 'myCustomLockName' + } name: 'aaddswaf001' pfxCertificate: '' pfxCertificatePassword: '' - replicaSets: '' + replicaSets: [ + { + location: 'NorthEurope' + subnetId: '' + } + ] sku: 'Standard' - tags: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } } } ``` @@ -89,10 +118,25 @@ module domainService 'br/public:avm/res/aad/domain-service:' = { ] }, "diagnosticSettings": { - "value": "" - }, - "enableTelemetry": { - "value": "" + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "category": "AllLogs" + } + ], + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] }, "externalAccess": { "value": "Enabled" @@ -104,7 +148,10 @@ module domainService 'br/public:avm/res/aad/domain-service:' = { "value": "" }, "lock": { - "value": "" + "value": { + "kind": "None", + "name": "myCustomLockName" + } }, "name": { "value": "aaddswaf001" @@ -116,13 +163,22 @@ module domainService 'br/public:avm/res/aad/domain-service:' = { "value": "" }, "replicaSets": { - "value": "" + "value": [ + { + "location": "NorthEurope", + "subnetId": "" + } + ] }, "sku": { "value": "Standard" }, "tags": { - "value": "" + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } } } } @@ -219,8 +275,9 @@ The diagnostic settings of the service. | [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | | [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | | [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | -| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | | [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | | [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | | [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | | [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | @@ -255,7 +312,7 @@ A string indicating whether the export to Log Analytics should use the default d ### Parameter: `diagnosticSettings.logCategoriesAndGroups` -The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. - Required: No - Type: array @@ -265,7 +322,8 @@ The name of logs that will be streamed. "allLogs" includes all possible logs for | Parameter | Type | Description | | :-- | :-- | :-- | | [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | -| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | ### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` @@ -276,11 +334,18 @@ Name of a Diagnostic Log category for a resource type this setting is applied to ### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` -Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. - Required: No - Type: string +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + ### Parameter: `diagnosticSettings.marketplacePartnerResourceId` The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. @@ -288,6 +353,39 @@ The full ARM resource ID of the Marketplace resource to which you would like to - Required: No - Type: string +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + ### Parameter: `diagnosticSettings.name` The name of diagnostic setting. diff --git a/avm/res/aad/domain-service/main.bicep b/avm/res/aad/domain-service/main.bicep index 1e178c17fd..cd0e56bfcd 100644 --- a/avm/res/aad/domain-service/main.bicep +++ b/avm/res/aad/domain-service/main.bicep @@ -3,7 +3,7 @@ metadata description = 'This module deploys an Azure Active Directory Domain Ser metadata owner = 'Azure/module-maintainers' @minLength(1) -@maxLength(19) // 15 characters for domain name + 4 characters for suffix +@maxLength(19) // 15 characters for domain name + 4 characters for the suffix @description('Optional. The name of the AADDS resource. Defaults to the domain name specific to the Azure ADDS service. The prefix of your specified domain name (such as dscontoso in the dscontoso.com domain name) must contain 15 or fewer characters.') param name string = domainName diff --git a/avm/res/aad/domain-service/main.json b/avm/res/aad/domain-service/main.json index d5362f9fcb..84a4f00d14 100644 --- a/avm/res/aad/domain-service/main.json +++ b/avm/res/aad/domain-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10981571743323253420" + "version": "0.25.53.49325", + "templateHash": "7386947428934165964" }, "name": "Azure Active Directory Domain Services", "description": "This module deploys an Azure Active Directory Domain Services (AADDS).", @@ -132,14 +132,46 @@ "type": "string", "nullable": true, "metadata": { - "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs." + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." } } } }, "nullable": true, "metadata": { - "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." } }, "logAnalyticsDestinationType": { @@ -519,11 +551,30 @@ "scope": "[format('Microsoft.AAD/domainServices/{0}', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "logs": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'AllLogs', 'enabled', true())))]", "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" }, From fbe5dab637ed415dd81a9763c48f6fe80a341b51 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Wed, 10 Apr 2024 12:33:50 +0200 Subject: [PATCH 15/66] fix: CI - Updated publish from tag to work with latest validation implementation (#1635) ## Description Updated to standard used by the `publishModule` action ## Pipeline Reference | Pipeline | | -------- | | [![avm.platform.publish-tag](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.platform.publish-tag.yml/badge.svg?branch=users%2Falsehr%2FpublishFromTagValidation&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.platform.publish-tag.yml) | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- .github/workflows/avm.platform.publish-tag.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/avm.platform.publish-tag.yml b/.github/workflows/avm.platform.publish-tag.yml index c8dd150deb..702fe493f2 100644 --- a/.github/workflows/avm.platform.publish-tag.yml +++ b/.github/workflows/avm.platform.publish-tag.yml @@ -63,9 +63,11 @@ jobs: Write-Verbose 'Invoke function with' -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose - if($publishOutputs = Publish-ModuleFromTagToPBR @functionInput -Verbose) { - Write-Output ('{0}={1}' -f 'version', $publishOutputs.version) >> $env:GITHUB_OUTPUT - Write-Output ('{0}={1}' -f 'publishedModuleName', $publishOutputs.publishedModuleName) >> $env:GITHUB_OUTPUT + if($publishOutputs = Publish-ModuleFromPathToPBR @functionInput -Verbose) { + $publishOutputs.Keys | Foreach-Object { + Write-Verbose ('Passing pipeline variable [{0}] with value [{1}]' -f $_, $publishOutputs.$_) -Verbose + Write-Output ('{0}={1}' -f $_, $publishOutputs.$_) >> $env:GITHUB_OUTPUT + } } Write-Output '::endgroup::' @@ -85,6 +87,8 @@ jobs: $functionInput = @{ Version = "${{ steps.publish_tag.outputs.version }}" PublishedModuleName = "${{ steps.publish_tag.outputs.publishedModuleName }}" + GitTagName = "${{ steps.publish_step.outputs.gitTagName }}" + } Write-Verbose "Invoke function with" -Verbose From 94ae6d1a0dde00a6e5f68f94faa89f23ad073668 Mon Sep 17 00:00:00 2001 From: snoejovich <92036998+snoejovich@users.noreply.github.com> Date: Wed, 10 Apr 2024 09:38:25 -0500 Subject: [PATCH 16/66] feat: `avm/res/service-fabric/cluster` (#1393) ## Description Migrate bicep module for service-fabric cluster from CARML. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.service-fabric.cluster](https://github.com/snoejovich/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml/badge.svg?branch=feature-sn-newmigrateservicefabric)](https://github.com/snoejovich/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Zach Trocinski <30884663+oZakari@users.noreply.github.com> Co-authored-by: Alexander Sehr Co-authored-by: Luke Snoddy <37806411+lsnoddy@users.noreply.github.com> Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.service-fabric.cluster.yml | 85 + avm/res/service-fabric/cluster/README.md | 1536 +++++++++++++++++ .../cluster/application-type/README.md | 71 + .../cluster/application-type/main.bicep | 31 + .../cluster/application-type/main.json | 77 + avm/res/service-fabric/cluster/main.bicep | 479 +++++ avm/res/service-fabric/cluster/main.json | 677 ++++++++ .../cluster/tests/e2e/cert/main.test.bicep | 71 + .../tests/e2e/defaults/main.test.bicep | 67 + .../cluster/tests/e2e/max/dependencies.bicep | 31 + .../cluster/tests/e2e/max/main.test.bicep | 238 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 31 + .../tests/e2e/waf-aligned/main.test.bicep | 214 +++ avm/res/service-fabric/cluster/version.json | 7 + 16 files changed, 3617 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.service-fabric.cluster.yml create mode 100644 avm/res/service-fabric/cluster/README.md create mode 100644 avm/res/service-fabric/cluster/application-type/README.md create mode 100644 avm/res/service-fabric/cluster/application-type/main.bicep create mode 100644 avm/res/service-fabric/cluster/application-type/main.json create mode 100644 avm/res/service-fabric/cluster/main.bicep create mode 100644 avm/res/service-fabric/cluster/main.json create mode 100644 avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/service-fabric/cluster/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index da03f81c8a..aeceb727dd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -126,7 +126,7 @@ #/avm/res/resources/tags/ @Azure/avm-res-resources-tags-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/search/search-service/ @Azure/avm-res-search-searchservice-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/service-bus/namespace/ @Azure/avm-res-servicebus-namespace-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/signal-r/ @Azure/avm-res-signalrservice-signalr-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/web-pub-sub/ @Azure/avm-res-signalrservice-webpubsub-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index cd6d3f20ce..a1f30bffbc 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -159,7 +159,7 @@ body: # - "avm/res/resources/tags" - "avm/res/search/search-service" - "avm/res/service-bus/namespace" - # - "avm/res/service-fabric/cluster" + - "avm/res/service-fabric/cluster" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" # - "avm/res/sql/managed-instance" diff --git a/.github/workflows/avm.res.service-fabric.cluster.yml b/.github/workflows/avm.res.service-fabric.cluster.yml new file mode 100644 index 0000000000..93c11bbf84 --- /dev/null +++ b/.github/workflows/avm.res.service-fabric.cluster.yml @@ -0,0 +1,85 @@ +name: "avm.res.service-fabric.cluster" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.service-fabric.cluster.yml" + - "avm/res/service-fabric/cluster/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/service-fabric/cluster" + workflowPath: ".github/workflows/avm.res.service-fabric.cluster.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get parameter file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/service-fabric/cluster/README.md b/avm/res/service-fabric/cluster/README.md new file mode 100644 index 0000000000..eba3b7a0b5 --- /dev/null +++ b/avm/res/service-fabric/cluster/README.md @@ -0,0 +1,1536 @@ +# Service Fabric Clusters `[Microsoft.ServiceFabric/clusters]` + +This module deploys a Service Fabric Cluster. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.ServiceFabric/clusters` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters) | +| `Microsoft.ServiceFabric/clusters/applicationTypes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters/applicationTypes) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/service-fabric/cluster:`. + +- [Certificate](#example-1-certificate) +- [Using only defaults](#example-2-using-only-defaults) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Certificate_ + +This instance deploys the module with a certificate. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfccer001.westeurope.cloudapp.azure.com:19080' + name: 'sfccer001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + reliabilityLevel: 'None' + // Non-required parameters + certificate: { + thumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + x509StoreName: 'My' + } + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfccer001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfccer001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + }, + "reliabilityLevel": { + "value": "None" + }, + // Non-required parameters + "certificate": { + "value": { + "thumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "x509StoreName": "My" + } + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfcmin001.westeurope.cloudapp.azure.com:19080' + name: 'sfcmin001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + reliabilityLevel: 'None' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfcmin001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfcmin001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "name": "Node01" + } + ] + }, + "reliabilityLevel": { + "value": "None" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfcmax001.westeurope.cloudapp.azure.com:19080' + name: 'sfcmax001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + isStateless: false + multipleAvailabilityZones: false + name: 'Node01' + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + startPort: 49000 + vmInstanceCount: 5 + } + } + ] + reliabilityLevel: 'Silver' + // Non-required parameters + addOnFeatures: [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + azureActiveDirectory: { + clientApplication: '' + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: '' + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: '' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: '' + storageAccountName: '' + tableEndpoint: '' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + maxUnusedVersionsToKeep: 2 + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + tags: { + clusterName: 'sfcmax001' + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + } + upgradeDescription: { + deltaHealthPolicy: { + maxPercentDeltaUnhealthyApplications: 0 + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + } + forceRestart: false + healthCheckRetryTimeout: '00:45:00' + healthCheckStableDuration: '00:01:00' + healthCheckWaitDuration: '00:00:30' + healthPolicy: { + maxPercentUnhealthyApplications: 0 + maxPercentUnhealthyNodes: 0 + } + upgradeDomainTimeout: '02:00:00' + upgradeReplicaSetCheckTimeout: '1.00:00:00' + upgradeTimeout: '02:00:00' + } + vmImage: 'Linux' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfcmax001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfcmax001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Silver", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "isStateless": false, + "multipleAvailabilityZones": false, + "name": "Node01", + "placementProperties": {}, + "reverseProxyEndpointPort": "", + "vmInstanceCount": 5 + }, + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 64000, + "httpGatewayEndpointPort": 19007, + "isPrimary": true, + "name": "Node02", + "startPort": 49000, + "vmInstanceCount": 5 + } + } + ] + }, + "reliabilityLevel": { + "value": "Silver" + }, + // Non-required parameters + "addOnFeatures": { + "value": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ] + }, + "applicationTypes": { + "value": [ + { + "name": "WordCount" + } + ] + }, + "azureActiveDirectory": { + "value": { + "clientApplication": "", + "clusterApplication": "cf33fea8-b30f-424f-ab73-c48d99e0b222", + "tenantId": "" + } + }, + "certificateCommonNames": { + "value": { + "commonNames": [ + { + "certificateCommonName": "certcommon", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130" + } + ], + "x509StoreName": "" + } + }, + "clientCertificateCommonNames": { + "value": [ + { + "certificateCommonName": "clientcommoncert1", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateCommonName": "clientcommoncert2", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "clientCertificateThumbprints": { + "value": [ + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "diagnosticsStorageAccountConfig": { + "value": { + "blobEndpoint": "", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "", + "storageAccountName": "", + "tableEndpoint": "" + } + }, + "fabricSettings": { + "value": [ + { + "name": "Security", + "parameters": [ + { + "name": "ClusterProtectionLevel", + "value": "EncryptAndSign" + } + ] + }, + { + "name": "UpgradeService", + "parameters": [ + { + "name": "AppPollIntervalInSeconds", + "value": "60" + } + ] + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "maxUnusedVersionsToKeep": { + "value": 2 + }, + "notifications": { + "value": [ + { + "isEnabled": true, + "notificationCategory": "WaveProgress", + "notificationLevel": "Critical", + "notificationTargets": [ + { + "notificationChannel": "EmailUser", + "receivers": [ + "SomeReceiver" + ] + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "tags": { + "value": { + "clusterName": "sfcmax001", + "hidden-title": "This is visible in the resource name", + "resourceType": "Service Fabric" + } + }, + "upgradeDescription": { + "value": { + "deltaHealthPolicy": { + "maxPercentDeltaUnhealthyApplications": 0, + "maxPercentDeltaUnhealthyNodes": 0, + "maxPercentUpgradeDomainDeltaUnhealthyNodes": 0 + }, + "forceRestart": false, + "healthCheckRetryTimeout": "00:45:00", + "healthCheckStableDuration": "00:01:00", + "healthCheckWaitDuration": "00:00:30", + "healthPolicy": { + "maxPercentUnhealthyApplications": 0, + "maxPercentUnhealthyNodes": 0 + }, + "upgradeDomainTimeout": "02:00:00", + "upgradeReplicaSetCheckTimeout": "1.00:00:00", + "upgradeTimeout": "02:00:00" + } + }, + "vmImage": { + "value": "Linux" + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module cluster 'br/public:avm/res/service-fabric/cluster:' = { + name: 'clusterDeployment' + params: { + // Required parameters + managementEndpoint: 'https://sfcwaf001.westeurope.cloudapp.azure.com:19080' + name: 'sfcwaf001' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + isStateless: false + multipleAvailabilityZones: false + name: 'Node01' + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + startPort: 49000 + vmInstanceCount: 5 + } + } + ] + reliabilityLevel: 'Silver' + // Non-required parameters + addOnFeatures: [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + azureActiveDirectory: { + clientApplication: '' + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: '' + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: '' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: '' + storageAccountName: '' + tableEndpoint: '' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + location: '' + maxUnusedVersionsToKeep: 2 + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + tags: { + clusterName: 'sfcwaf001' + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + } + upgradeDescription: { + deltaHealthPolicy: { + maxPercentDeltaUnhealthyApplications: 0 + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + } + forceRestart: false + healthCheckRetryTimeout: '00:45:00' + healthCheckStableDuration: '00:01:00' + healthCheckWaitDuration: '00:00:30' + healthPolicy: { + maxPercentUnhealthyApplications: 0 + maxPercentUnhealthyNodes: 0 + } + upgradeDomainTimeout: '02:00:00' + upgradeReplicaSetCheckTimeout: '1.00:00:00' + upgradeTimeout: '02:00:00' + } + vmImage: 'Linux' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "managementEndpoint": { + "value": "https://sfcwaf001.westeurope.cloudapp.azure.com:19080" + }, + "name": { + "value": "sfcwaf001" + }, + "nodeTypes": { + "value": [ + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Silver", + "ephemeralPorts": { + "endPort": 65534, + "startPort": 49152 + }, + "httpGatewayEndpointPort": 19080, + "isPrimary": true, + "isStateless": false, + "multipleAvailabilityZones": false, + "name": "Node01", + "placementProperties": {}, + "reverseProxyEndpointPort": "", + "vmInstanceCount": 5 + }, + { + "applicationPorts": { + "endPort": 30000, + "startPort": 20000 + }, + "clientConnectionEndpointPort": 19000, + "durabilityLevel": "Bronze", + "ephemeralPorts": { + "endPort": 64000, + "httpGatewayEndpointPort": 19007, + "isPrimary": true, + "name": "Node02", + "startPort": 49000, + "vmInstanceCount": 5 + } + } + ] + }, + "reliabilityLevel": { + "value": "Silver" + }, + // Non-required parameters + "addOnFeatures": { + "value": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ] + }, + "applicationTypes": { + "value": [ + { + "name": "WordCount" + } + ] + }, + "azureActiveDirectory": { + "value": { + "clientApplication": "", + "clusterApplication": "cf33fea8-b30f-424f-ab73-c48d99e0b222", + "tenantId": "" + } + }, + "certificateCommonNames": { + "value": { + "commonNames": [ + { + "certificateCommonName": "certcommon", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130" + } + ], + "x509StoreName": "" + } + }, + "clientCertificateCommonNames": { + "value": [ + { + "certificateCommonName": "clientcommoncert1", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateCommonName": "clientcommoncert2", + "certificateIssuerThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "clientCertificateThumbprints": { + "value": [ + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC130", + "isAdmin": false + }, + { + "certificateThumbprint": "0AC113D5E1D94C401DDEB0EE2B1B96CC131", + "isAdmin": false + } + ] + }, + "diagnosticsStorageAccountConfig": { + "value": { + "blobEndpoint": "", + "protectedAccountKeyName": "StorageAccountKey1", + "queueEndpoint": "", + "storageAccountName": "", + "tableEndpoint": "" + } + }, + "fabricSettings": { + "value": [ + { + "name": "Security", + "parameters": [ + { + "name": "ClusterProtectionLevel", + "value": "EncryptAndSign" + } + ] + }, + { + "name": "UpgradeService", + "parameters": [ + { + "name": "AppPollIntervalInSeconds", + "value": "60" + } + ] + } + ] + }, + "location": { + "value": "" + }, + "maxUnusedVersionsToKeep": { + "value": 2 + }, + "notifications": { + "value": [ + { + "isEnabled": true, + "notificationCategory": "WaveProgress", + "notificationLevel": "Critical", + "notificationTargets": [ + { + "notificationChannel": "EmailUser", + "receivers": [ + "SomeReceiver" + ] + } + ] + } + ] + }, + "tags": { + "value": { + "clusterName": "sfcwaf001", + "hidden-title": "This is visible in the resource name", + "resourceType": "Service Fabric" + } + }, + "upgradeDescription": { + "value": { + "deltaHealthPolicy": { + "maxPercentDeltaUnhealthyApplications": 0, + "maxPercentDeltaUnhealthyNodes": 0, + "maxPercentUpgradeDomainDeltaUnhealthyNodes": 0 + }, + "forceRestart": false, + "healthCheckRetryTimeout": "00:45:00", + "healthCheckStableDuration": "00:01:00", + "healthCheckWaitDuration": "00:00:30", + "healthPolicy": { + "maxPercentUnhealthyApplications": 0, + "maxPercentUnhealthyNodes": 0 + }, + "upgradeDomainTimeout": "02:00:00", + "upgradeReplicaSetCheckTimeout": "1.00:00:00", + "upgradeTimeout": "02:00:00" + } + }, + "vmImage": { + "value": "Linux" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managementEndpoint`](#parameter-managementendpoint) | string | The http management endpoint of the cluster. | +| [`name`](#parameter-name) | string | Name of the Service Fabric cluster. | +| [`nodeTypes`](#parameter-nodetypes) | array | The list of node types in the cluster. | +| [`reliabilityLevel`](#parameter-reliabilitylevel) | string | The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addOnFeatures`](#parameter-addonfeatures) | array | The list of add-on features to enable in the cluster. | +| [`applicationTypes`](#parameter-applicationtypes) | array | Array of Service Fabric cluster application types. | +| [`azureActiveDirectory`](#parameter-azureactivedirectory) | object | The settings to enable AAD authentication on the cluster. | +| [`certificate`](#parameter-certificate) | object | Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location. | +| [`certificateCommonNames`](#parameter-certificatecommonnames) | object | Describes a list of server certificates referenced by common name that are used to secure the cluster. | +| [`clientCertificateCommonNames`](#parameter-clientcertificatecommonnames) | array | The list of client certificates referenced by common name that are allowed to manage the cluster. | +| [`clientCertificateThumbprints`](#parameter-clientcertificatethumbprints) | array | The list of client certificates referenced by thumbprint that are allowed to manage the cluster. | +| [`clusterCodeVersion`](#parameter-clustercodeversion) | string | The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions. | +| [`diagnosticsStorageAccountConfig`](#parameter-diagnosticsstorageaccountconfig) | object | The storage account information for storing Service Fabric diagnostic logs. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`eventStoreServiceEnabled`](#parameter-eventstoreserviceenabled) | bool | Indicates if the event store service is enabled. | +| [`fabricSettings`](#parameter-fabricsettings) | array | The list of custom fabric settings to configure the cluster. | +| [`infrastructureServiceManager`](#parameter-infrastructureservicemanager) | bool | Indicates if infrastructure service manager is enabled. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`maxUnusedVersionsToKeep`](#parameter-maxunusedversionstokeep) | int | Number of unused versions per application type to keep. | +| [`notifications`](#parameter-notifications) | array | Indicates a list of notification channels for cluster events. | +| [`reverseProxyCertificate`](#parameter-reverseproxycertificate) | object | Describes the certificate details. | +| [`reverseProxyCertificateCommonNames`](#parameter-reverseproxycertificatecommonnames) | object | Describes a list of server certificates referenced by common name that are used to secure the cluster. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`sfZonalUpgradeMode`](#parameter-sfzonalupgrademode) | string | This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`upgradeDescription`](#parameter-upgradedescription) | object | Describes the policy used when upgrading the cluster. | +| [`upgradeMode`](#parameter-upgrademode) | string | The upgrade mode of the cluster when new Service Fabric runtime version is available. | +| [`upgradePauseEndTimestampUtc`](#parameter-upgradepauseendtimestamputc) | string | Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). | +| [`upgradePauseStartTimestampUtc`](#parameter-upgradepausestarttimestamputc) | string | Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). | +| [`upgradeWave`](#parameter-upgradewave) | string | Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0. | +| [`vmImage`](#parameter-vmimage) | string | The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used. | +| [`vmssZonalUpgradeMode`](#parameter-vmsszonalupgrademode) | string | This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added. | +| [`waveUpgradePaused`](#parameter-waveupgradepaused) | bool | Boolean to pause automatic runtime version upgrades to the cluster. | + +### Parameter: `managementEndpoint` + +The http management endpoint of the cluster. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Name of the Service Fabric cluster. + +- Required: Yes +- Type: string + +### Parameter: `nodeTypes` + +The list of node types in the cluster. + +- Required: Yes +- Type: array + +### Parameter: `reliabilityLevel` + +The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Bronze' + 'Gold' + 'None' + 'Platinum' + 'Silver' + ] + ``` + +### Parameter: `addOnFeatures` + +The list of add-on features to enable in the cluster. + +- Required: No +- Type: array +- Default: `[]` +- Allowed: + ```Bicep + [ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' + ] + ``` + +### Parameter: `applicationTypes` + +Array of Service Fabric cluster application types. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `azureActiveDirectory` + +The settings to enable AAD authentication on the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `certificate` + +Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `certificateCommonNames` + +Describes a list of server certificates referenced by common name that are used to secure the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `clientCertificateCommonNames` + +The list of client certificates referenced by common name that are allowed to manage the cluster. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `clientCertificateThumbprints` + +The list of client certificates referenced by thumbprint that are allowed to manage the cluster. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `clusterCodeVersion` + +The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions. + +- Required: No +- Type: string + +### Parameter: `diagnosticsStorageAccountConfig` + +The storage account information for storing Service Fabric diagnostic logs. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `eventStoreServiceEnabled` + +Indicates if the event store service is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `fabricSettings` + +The list of custom fabric settings to configure the cluster. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `infrastructureServiceManager` + +Indicates if infrastructure service manager is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `maxUnusedVersionsToKeep` + +Number of unused versions per application type to keep. + +- Required: No +- Type: int +- Default: `3` + +### Parameter: `notifications` + +Indicates a list of notification channels for cluster events. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `reverseProxyCertificate` + +Describes the certificate details. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `reverseProxyCertificateCommonNames` + +Describes a list of server certificates referenced by common name that are used to secure the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `sfZonalUpgradeMode` + +This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster. + +- Required: No +- Type: string +- Default: `'Hierarchical'` +- Allowed: + ```Bicep + [ + 'Hierarchical' + 'Parallel' + ] + ``` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `upgradeDescription` + +Describes the policy used when upgrading the cluster. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `upgradeMode` + +The upgrade mode of the cluster when new Service Fabric runtime version is available. + +- Required: No +- Type: string +- Default: `'Automatic'` +- Allowed: + ```Bicep + [ + 'Automatic' + 'Manual' + ] + ``` + +### Parameter: `upgradePauseEndTimestampUtc` + +Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). + +- Required: No +- Type: string + +### Parameter: `upgradePauseStartTimestampUtc` + +Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC). + +- Required: No +- Type: string + +### Parameter: `upgradeWave` + +Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0. + +- Required: No +- Type: string +- Default: `'Wave0'` +- Allowed: + ```Bicep + [ + 'Wave0' + 'Wave1' + 'Wave2' + ] + ``` + +### Parameter: `vmImage` + +The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used. + +- Required: No +- Type: string + +### Parameter: `vmssZonalUpgradeMode` + +This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added. + +- Required: No +- Type: string +- Default: `'Hierarchical'` +- Allowed: + ```Bicep + [ + 'Hierarchical' + 'Parallel' + ] + ``` + +### Parameter: `waveUpgradePaused` + +Boolean to pause automatic runtime version upgrades to the cluster. + +- Required: No +- Type: bool +- Default: `False` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `endpoint` | string | The Service Fabric Cluster endpoint. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Service Fabric Cluster name. | +| `resourceGroupName` | string | The Service Fabric Cluster resource group. | +| `resourceId` | string | The Service Fabric Cluster resource ID. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/service-fabric/cluster/application-type/README.md b/avm/res/service-fabric/cluster/application-type/README.md new file mode 100644 index 0000000000..c9d50e5414 --- /dev/null +++ b/avm/res/service-fabric/cluster/application-type/README.md @@ -0,0 +1,71 @@ +# Service Fabric Cluster Application Types `[Microsoft.ServiceFabric/clusters/applicationTypes]` + +This module deploys a Service Fabric Cluster Application Type. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceFabric/clusters/applicationTypes` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceFabric/2021-06-01/clusters/applicationTypes) | + +## Parameters + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serviceFabricClusterName`](#parameter-servicefabricclustername) | string | The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Application type name. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `serviceFabricClusterName` + +The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Application type name. + +- Required: No +- Type: string +- Default: `'defaultApplicationType'` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The resource name of the Application type. | +| `resourceGroupName` | string | The resource group of the Application type. | +| `resourceID` | string | The resource ID of the Application type. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/service-fabric/cluster/application-type/main.bicep b/avm/res/service-fabric/cluster/application-type/main.bicep new file mode 100644 index 0000000000..63dec4bc25 --- /dev/null +++ b/avm/res/service-fabric/cluster/application-type/main.bicep @@ -0,0 +1,31 @@ +metadata name = 'Service Fabric Cluster Application Types' +metadata description = 'This module deploys a Service Fabric Cluster Application Type.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment.') +param serviceFabricClusterName string + +@description('Optional. Application type name.') +param name string = 'defaultApplicationType' + +@description('Optional. Tags of the resource.') +param tags object? + +resource serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' existing = { + name: serviceFabricClusterName +} + +resource applicationTypes 'Microsoft.ServiceFabric/clusters/applicationTypes@2021-06-01' = { + name: name + parent: serviceFabricCluster + tags: tags +} + +@description('The resource name of the Application type.') +output name string = applicationTypes.name + +@description('The resource group of the Application type.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Application type.') +output resourceID string = applicationTypes.id diff --git a/avm/res/service-fabric/cluster/application-type/main.json b/avm/res/service-fabric/cluster/application-type/main.json new file mode 100644 index 0000000000..0e9d6a25b4 --- /dev/null +++ b/avm/res/service-fabric/cluster/application-type/main.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4241007545422374207" + }, + "name": "Service Fabric Cluster Application Types", + "description": "This module deploys a Service Fabric Cluster Application Type.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "serviceFabricClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "defaultApplicationType", + "metadata": { + "description": "Optional. Application type name." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "serviceFabricCluster": { + "existing": true, + "type": "Microsoft.ServiceFabric/clusters", + "apiVersion": "2021-06-01", + "name": "[parameters('serviceFabricClusterName')]" + }, + "applicationTypes": { + "type": "Microsoft.ServiceFabric/clusters/applicationTypes", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('serviceFabricClusterName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "dependsOn": [ + "serviceFabricCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The resource name of the Application type." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the Application type." + }, + "value": "[resourceGroup().name]" + }, + "resourceID": { + "type": "string", + "metadata": { + "description": "The resource ID of the Application type." + }, + "value": "[resourceId('Microsoft.ServiceFabric/clusters/applicationTypes', parameters('serviceFabricClusterName'), parameters('name'))]" + } + } +} \ No newline at end of file diff --git a/avm/res/service-fabric/cluster/main.bicep b/avm/res/service-fabric/cluster/main.bicep new file mode 100644 index 0000000000..d2bcd4e614 --- /dev/null +++ b/avm/res/service-fabric/cluster/main.bicep @@ -0,0 +1,479 @@ +metadata name = 'Service Fabric Clusters' +metadata description = 'This module deploys a Service Fabric Cluster.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the Service Fabric cluster.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. The lock settings of the service.') +param lock lockType + +@allowed([ + 'BackupRestoreService' + 'DnsService' + 'RepairManager' + 'ResourceMonitorService' +]) +@description('Optional. The list of add-on features to enable in the cluster.') +param addOnFeatures array = [] + +@description('Optional. Number of unused versions per application type to keep.') +param maxUnusedVersionsToKeep int = 3 + +@description('Optional. The settings to enable AAD authentication on the cluster.') +param azureActiveDirectory object = {} + +@description('Optional. Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location.') +param certificate object = {} + +@description('Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster.') +param certificateCommonNames object = {} + +@description('Optional. The list of client certificates referenced by common name that are allowed to manage the cluster.') +param clientCertificateCommonNames array = [] + +@description('Optional. The list of client certificates referenced by thumbprint that are allowed to manage the cluster.') +param clientCertificateThumbprints array = [] + +@description('Optional. The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to "Manual". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions.') +param clusterCodeVersion string? + +@description('Optional. The storage account information for storing Service Fabric diagnostic logs.') +param diagnosticsStorageAccountConfig object = {} + +@description('Optional. Indicates if the event store service is enabled.') +param eventStoreServiceEnabled bool = false + +@description('Optional. The list of custom fabric settings to configure the cluster.') +param fabricSettings array = [] + +@description('Optional. Indicates if infrastructure service manager is enabled.') +param infrastructureServiceManager bool = false + +@description('Required. The http management endpoint of the cluster.') +param managementEndpoint string + +@description('Required. The list of node types in the cluster.') +param nodeTypes array + +@description('Optional. Indicates a list of notification channels for cluster events.') +param notifications array = [] + +@allowed([ + 'Bronze' + 'Gold' + 'None' + 'Platinum' + 'Silver' +]) +@description('Required. The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9.') +param reliabilityLevel string + +@description('Optional. Describes the certificate details.') +param reverseProxyCertificate object = {} + +@description('Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster.') +param reverseProxyCertificateCommonNames object = {} + +@allowed([ + 'Hierarchical' + 'Parallel' +]) +@description('Optional. This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster.') +param sfZonalUpgradeMode string = 'Hierarchical' + +@description('Optional. Describes the policy used when upgrading the cluster.') +param upgradeDescription object = {} + +@allowed([ + 'Automatic' + 'Manual' +]) +@description('Optional. The upgrade mode of the cluster when new Service Fabric runtime version is available.') +param upgradeMode string = 'Automatic' + +@description('Optional. Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC).') +param upgradePauseEndTimestampUtc string? + +@description('Optional. Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC).') +param upgradePauseStartTimestampUtc string? + +@allowed([ + 'Wave0' + 'Wave1' + 'Wave2' +]) +@description('Optional. Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0.') +param upgradeWave string = 'Wave0' + +@description('Optional. The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used.') +param vmImage string? + +@allowed([ + 'Hierarchical' + 'Parallel' +]) +@description('Optional. This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added.') +param vmssZonalUpgradeMode string = 'Hierarchical' + +@description('Optional. Boolean to pause automatic runtime version upgrades to the cluster.') +param waveUpgradePaused bool = false + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Array of Service Fabric cluster application types.') +param applicationTypes array = [] + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var clientCertificateCommonNamesVar = [ + for clientCertificateCommonName in clientCertificateCommonNames: { + certificateCommonName: contains(clientCertificateCommonName, 'certificateCommonName') + ? clientCertificateCommonName.certificateCommonName + : null + certificateIssuerThumbprint: contains(clientCertificateCommonName, 'certificateIssuerThumbprint') + ? clientCertificateCommonName.certificateIssuerThumbprint + : null + isAdmin: contains(clientCertificateCommonName, 'isAdmin') ? clientCertificateCommonName.isAdmin : false + } +] + +var clientCertificateThumbprintsVar = [ + for clientCertificateThumbprint in clientCertificateThumbprints: { + certificateThumbprint: contains(clientCertificateThumbprint, 'certificateThumbprint') + ? clientCertificateThumbprint.certificateThumbprint + : null + isAdmin: contains(clientCertificateThumbprint, 'isAdmin') ? clientCertificateThumbprint.isAdmin : false + } +] + +var fabricSettingsVar = [ + for fabricSetting in fabricSettings: { + name: contains(fabricSetting, 'name') ? fabricSetting.name : null + parameters: contains(fabricSetting, 'parameters') ? fabricSetting.parameters : null + } +] + +var fnodeTypesVar = [ + for nodeType in nodeTypes: { + applicationPorts: contains(nodeType, 'applicationPorts') + ? { + endPort: contains(nodeType.applicationPorts, 'endPort') ? nodeType.applicationPorts.endPort : null + startPort: contains(nodeType.applicationPorts, 'startPort') ? nodeType.applicationPorts.startPort : null + } + : null + capacities: contains(nodeType, 'capacities') ? nodeType.capacities : null + clientConnectionEndpointPort: contains(nodeType, 'clientConnectionEndpointPort') + ? nodeType.clientConnectionEndpointPort + : null + durabilityLevel: contains(nodeType, 'durabilityLevel') ? nodeType.durabilityLevel : null + ephemeralPorts: contains(nodeType, 'ephemeralPorts') + ? { + endPort: contains(nodeType.ephemeralPorts, 'endPort') ? nodeType.ephemeralPorts.endPort : null + startPort: contains(nodeType.ephemeralPorts, 'startPort') ? nodeType.ephemeralPorts.startPort : null + } + : null + httpGatewayEndpointPort: contains(nodeType, 'httpGatewayEndpointPort') ? nodeType.httpGatewayEndpointPort : null + isPrimary: contains(nodeType, 'isPrimary') ? nodeType.isPrimary : null + isStateless: contains(nodeType, 'isStateless') ? nodeType.isStateless : null + multipleAvailabilityZones: contains(nodeType, 'multipleAvailabilityZones') + ? nodeType.multipleAvailabilityZones + : null + name: contains(nodeType, 'name') ? nodeType.name : 'Node00' + placementProperties: contains(nodeType, 'placementProperties') ? nodeType.placementProperties : null + reverseProxyEndpointPort: contains(nodeType, 'reverseProxyEndpointPort') ? nodeType.reverseProxyEndpointPort : null + vmInstanceCount: contains(nodeType, 'vmInstanceCount') ? nodeType.vmInstanceCount : 1 + } +] + +var notificationsVar = [ + for notification in notifications: { + isEnabled: contains(notification, 'isEnabled') ? notification.isEnabled : false + notificationCategory: contains(notification, 'notificationCategory') + ? notification.notificationCategory + : 'WaveProgress' + notificationLevel: contains(notification, 'notificationLevel') ? notification.notificationLevel : 'All' + notificationTargets: contains(notification, 'notificationTargets') ? notification.notificationTargets : [] + } +] + +var upgradeDescriptionVar = union( + { + deltaHealthPolicy: { + applicationDeltaHealthPolicies: upgradeDescription.?applicationDeltaHealthPolicies ?? {} + maxPercentDeltaUnhealthyApplications: upgradeDescription.?maxPercentDeltaUnhealthyApplications ?? 0 + maxPercentDeltaUnhealthyNodes: upgradeDescription.?maxPercentDeltaUnhealthyNodes ?? 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: upgradeDescription.?maxPercentUpgradeDomainDeltaUnhealthyNodes ?? 0 + } + forceRestart: contains(upgradeDescription, 'forceRestart') ? upgradeDescription.forceRestart : false + healthCheckRetryTimeout: contains(upgradeDescription, 'healthCheckRetryTimeout') + ? upgradeDescription.healthCheckRetryTimeout + : '00:45:00' + healthCheckStableDuration: contains(upgradeDescription, 'healthCheckStableDuration') + ? upgradeDescription.healthCheckStableDuration + : '00:01:00' + healthCheckWaitDuration: contains(upgradeDescription, 'healthCheckWaitDuration') + ? upgradeDescription.healthCheckWaitDuration + : '00:00:30' + upgradeDomainTimeout: contains(upgradeDescription, 'upgradeDomainTimeout') + ? upgradeDescription.upgradeDomainTimeout + : '02:00:00' + upgradeReplicaSetCheckTimeout: contains(upgradeDescription, 'upgradeReplicaSetCheckTimeout') + ? upgradeDescription.upgradeReplicaSetCheckTimeout + : '1.00:00:00' + upgradeTimeout: contains(upgradeDescription, 'upgradeTimeout') ? upgradeDescription.upgradeTimeout : '02:00:00' + }, + contains(upgradeDescription, 'healthPolicy') + ? { + healthPolicy: { + applicationHealthPolicies: contains(upgradeDescription.healthPolicy, 'applicationHealthPolicies') + ? upgradeDescription.healthPolicy.applicationHealthPolicies + : {} + maxPercentUnhealthyApplications: contains(upgradeDescription.healthPolicy, 'maxPercentUnhealthyApplications') + ? upgradeDescription.healthPolicy.maxPercentUnhealthyApplications + : 0 + maxPercentUnhealthyNodes: contains(upgradeDescription.healthPolicy, 'maxPercentUnhealthyNodes') + ? upgradeDescription.healthPolicy.maxPercentUnhealthyNodes + : 0 + } + } + : {} +) + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.servicefabric-cluster.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +// Service Fabric cluster resource +resource serviceFabricCluster 'Microsoft.ServiceFabric/clusters@2021-06-01' = { + name: name + location: location + tags: tags + properties: { + addOnFeatures: addOnFeatures + applicationTypeVersionsCleanupPolicy: { + maxUnusedVersionsToKeep: maxUnusedVersionsToKeep + } + azureActiveDirectory: !empty(azureActiveDirectory) + ? { + clientApplication: contains(azureActiveDirectory, 'clientApplication') + ? azureActiveDirectory.clientApplication + : null + clusterApplication: contains(azureActiveDirectory, 'clusterApplication') + ? azureActiveDirectory.clusterApplication + : null + tenantId: contains(azureActiveDirectory, 'tenantId') ? azureActiveDirectory.tenantId : null + } + : null + certificate: !empty(certificate) + ? { + thumbprint: contains(certificate, 'thumbprint') ? certificate.thumbprint : null + thumbprintSecondary: contains(certificate, 'thumbprintSecondary') ? certificate.thumbprintSecondary : null + x509StoreName: contains(certificate, 'x509StoreName') ? certificate.x509StoreName : null + } + : null + certificateCommonNames: !empty(certificateCommonNames) + ? { + commonNames: contains(certificateCommonNames, 'commonNames') ? certificateCommonNames.commonNames : null + x509StoreName: contains(certificateCommonNames, 'certificateCommonNamesx509StoreName') + ? certificateCommonNames.certificateCommonNamesx509StoreName + : null + } + : null + clientCertificateCommonNames: !empty(clientCertificateCommonNames) ? clientCertificateCommonNamesVar : null + clientCertificateThumbprints: !empty(clientCertificateThumbprints) ? clientCertificateThumbprintsVar : null + clusterCodeVersion: clusterCodeVersion + diagnosticsStorageAccountConfig: !empty(diagnosticsStorageAccountConfig) + ? { + blobEndpoint: contains(diagnosticsStorageAccountConfig, 'blobEndpoint') + ? diagnosticsStorageAccountConfig.blobEndpoint + : null + protectedAccountKeyName: contains(diagnosticsStorageAccountConfig, 'protectedAccountKeyName') + ? diagnosticsStorageAccountConfig.protectedAccountKeyName + : null + protectedAccountKeyName2: contains(diagnosticsStorageAccountConfig, 'protectedAccountKeyName2') + ? diagnosticsStorageAccountConfig.protectedAccountKeyName2 + : null + queueEndpoint: contains(diagnosticsStorageAccountConfig, 'queueEndpoint') + ? diagnosticsStorageAccountConfig.queueEndpoint + : null + storageAccountName: contains(diagnosticsStorageAccountConfig, 'storageAccountName') + ? diagnosticsStorageAccountConfig.storageAccountName + : null + tableEndpoint: contains(diagnosticsStorageAccountConfig, 'tableEndpoint') + ? diagnosticsStorageAccountConfig.tableEndpoint + : null + } + : null + eventStoreServiceEnabled: eventStoreServiceEnabled + fabricSettings: !empty(fabricSettings) ? fabricSettingsVar : null + infrastructureServiceManager: infrastructureServiceManager + managementEndpoint: managementEndpoint + nodeTypes: !empty(nodeTypes) ? fnodeTypesVar : [] + notifications: !empty(notifications) ? notificationsVar : null + reliabilityLevel: !empty(reliabilityLevel) ? reliabilityLevel : 'None' + reverseProxyCertificate: !empty(reverseProxyCertificate) + ? { + thumbprint: contains(reverseProxyCertificate, 'thumbprint') ? reverseProxyCertificate.thumbprint : null + thumbprintSecondary: contains(reverseProxyCertificate, 'thumbprintSecondary') + ? reverseProxyCertificate.thumbprintSecondary + : null + x509StoreName: contains(reverseProxyCertificate, 'x509StoreName') + ? reverseProxyCertificate.x509StoreName + : null + } + : null + reverseProxyCertificateCommonNames: !empty(reverseProxyCertificateCommonNames) + ? { + commonNames: contains(reverseProxyCertificateCommonNames, 'commonNames') + ? reverseProxyCertificateCommonNames.commonNames + : null + x509StoreName: contains(reverseProxyCertificateCommonNames, 'x509StoreName') + ? reverseProxyCertificateCommonNames.x509StoreName + : null + } + : null + sfZonalUpgradeMode: !empty(sfZonalUpgradeMode) ? sfZonalUpgradeMode : null + upgradeDescription: !empty(upgradeDescription) ? upgradeDescriptionVar : null + upgradeMode: !empty(upgradeMode) ? upgradeMode : null + upgradePauseEndTimestampUtc: upgradePauseEndTimestampUtc + upgradePauseStartTimestampUtc: upgradePauseStartTimestampUtc + upgradeWave: !empty(upgradeWave) ? upgradeWave : null + vmImage: vmImage + vmssZonalUpgradeMode: !empty(vmssZonalUpgradeMode) ? vmssZonalUpgradeMode : null + waveUpgradePaused: waveUpgradePaused + } +} + +// Service Fabric cluster resource lock +resource serviceFabricCluster_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: serviceFabricCluster + } + +// Service Fabric cluster RBAC assignment +resource serviceFabricCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(serviceFabricCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: serviceFabricCluster + } +] + +// Service Fabric cluster application types +module serviceFabricCluster_applicationTypes 'application-type/main.bicep' = [ + for applicationType in applicationTypes: { + name: '${uniqueString(deployment().name, location)}-SFC-${applicationType.name}' + params: { + name: applicationType.name + serviceFabricClusterName: serviceFabricCluster.name + tags: applicationType.?tags ?? tags + } + } +] + +@description('The Service Fabric Cluster name.') +output name string = serviceFabricCluster.name + +@description('The Service Fabric Cluster resource group.') +output resourceGroupName string = resourceGroup().name + +@description('The Service Fabric Cluster resource ID.') +output resourceId string = serviceFabricCluster.id + +@description('The Service Fabric Cluster endpoint.') +output endpoint string = serviceFabricCluster.properties.clusterEndpoint + +@description('The location the resource was deployed into.') +output location string = serviceFabricCluster.location + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @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? + + @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? +}[]? diff --git a/avm/res/service-fabric/cluster/main.json b/avm/res/service-fabric/cluster/main.json new file mode 100644 index 0000000000..07ec9d6764 --- /dev/null +++ b/avm/res/service-fabric/cluster/main.json @@ -0,0 +1,677 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "2708589601167951653" + }, + "name": "Service Fabric Clusters", + "description": "This module deploys a Service Fabric Cluster.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Service Fabric cluster." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "addOnFeatures": { + "type": "array", + "defaultValue": [], + "allowedValues": [ + "BackupRestoreService", + "DnsService", + "RepairManager", + "ResourceMonitorService" + ], + "metadata": { + "description": "Optional. The list of add-on features to enable in the cluster." + } + }, + "maxUnusedVersionsToKeep": { + "type": "int", + "defaultValue": 3, + "metadata": { + "description": "Optional. Number of unused versions per application type to keep." + } + }, + "azureActiveDirectory": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The settings to enable AAD authentication on the cluster." + } + }, + "certificate": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes the certificate details like thumbprint of the primary certificate, thumbprint of the secondary certificate and the local certificate store location." + } + }, + "certificateCommonNames": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster." + } + }, + "clientCertificateCommonNames": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of client certificates referenced by common name that are allowed to manage the cluster." + } + }, + "clientCertificateThumbprints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of client certificates referenced by thumbprint that are allowed to manage the cluster." + } + }, + "clusterCodeVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Service Fabric runtime version of the cluster. This property can only by set the user when upgradeMode is set to \"Manual\". To get list of available Service Fabric versions for new clusters use ClusterVersion API. To get the list of available version for existing clusters use availableClusterVersions." + } + }, + "diagnosticsStorageAccountConfig": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The storage account information for storing Service Fabric diagnostic logs." + } + }, + "eventStoreServiceEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if the event store service is enabled." + } + }, + "fabricSettings": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of custom fabric settings to configure the cluster." + } + }, + "infrastructureServiceManager": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if infrastructure service manager is enabled." + } + }, + "managementEndpoint": { + "type": "string", + "metadata": { + "description": "Required. The http management endpoint of the cluster." + } + }, + "nodeTypes": { + "type": "array", + "metadata": { + "description": "Required. The list of node types in the cluster." + } + }, + "notifications": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Indicates a list of notification channels for cluster events." + } + }, + "reliabilityLevel": { + "type": "string", + "allowedValues": [ + "Bronze", + "Gold", + "None", + "Platinum", + "Silver" + ], + "metadata": { + "description": "Required. The reliability level sets the replica set size of system services. Learn about ReliabilityLevel (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-capacity). - None - Run the System services with a target replica set count of 1. This should only be used for test clusters. - Bronze - Run the System services with a target replica set count of 3. This should only be used for test clusters. - Silver - Run the System services with a target replica set count of 5. - Gold - Run the System services with a target replica set count of 7. - Platinum - Run the System services with a target replica set count of 9." + } + }, + "reverseProxyCertificate": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes the certificate details." + } + }, + "reverseProxyCertificateCommonNames": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes a list of server certificates referenced by common name that are used to secure the cluster." + } + }, + "sfZonalUpgradeMode": { + "type": "string", + "defaultValue": "Hierarchical", + "allowedValues": [ + "Hierarchical", + "Parallel" + ], + "metadata": { + "description": "Optional. This property controls the logical grouping of VMs in upgrade domains (UDs). This property cannot be modified if a node type with multiple Availability Zones is already present in the cluster." + } + }, + "upgradeDescription": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Describes the policy used when upgrading the cluster." + } + }, + "upgradeMode": { + "type": "string", + "defaultValue": "Automatic", + "allowedValues": [ + "Automatic", + "Manual" + ], + "metadata": { + "description": "Optional. The upgrade mode of the cluster when new Service Fabric runtime version is available." + } + }, + "upgradePauseEndTimestampUtc": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the end date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC)." + } + }, + "upgradePauseStartTimestampUtc": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the start date and time to pause automatic runtime version upgrades on the cluster for an specific period of time on the cluster (UTC)." + } + }, + "upgradeWave": { + "type": "string", + "defaultValue": "Wave0", + "allowedValues": [ + "Wave0", + "Wave1", + "Wave2" + ], + "metadata": { + "description": "Optional. Indicates when new cluster runtime version upgrades will be applied after they are released. By default is Wave0." + } + }, + "vmImage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The VM image VMSS has been configured with. Generic names such as Windows or Linux can be used." + } + }, + "vmssZonalUpgradeMode": { + "type": "string", + "defaultValue": "Hierarchical", + "allowedValues": [ + "Hierarchical", + "Parallel" + ], + "metadata": { + "description": "Optional. This property defines the upgrade mode for the virtual machine scale set, it is mandatory if a node type with multiple Availability Zones is added." + } + }, + "waveUpgradePaused": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Boolean to pause automatic runtime version upgrades to the cluster." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "applicationTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Service Fabric cluster application types." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "clientCertificateCommonNamesVar", + "count": "[length(parameters('clientCertificateCommonNames'))]", + "input": { + "certificateCommonName": "[if(contains(parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')], 'certificateCommonName'), parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')].certificateCommonName, null())]", + "certificateIssuerThumbprint": "[if(contains(parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')], 'certificateIssuerThumbprint'), parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')].certificateIssuerThumbprint, null())]", + "isAdmin": "[if(contains(parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')], 'isAdmin'), parameters('clientCertificateCommonNames')[copyIndex('clientCertificateCommonNamesVar')].isAdmin, false())]" + } + }, + { + "name": "clientCertificateThumbprintsVar", + "count": "[length(parameters('clientCertificateThumbprints'))]", + "input": { + "certificateThumbprint": "[if(contains(parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')], 'certificateThumbprint'), parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')].certificateThumbprint, null())]", + "isAdmin": "[if(contains(parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')], 'isAdmin'), parameters('clientCertificateThumbprints')[copyIndex('clientCertificateThumbprintsVar')].isAdmin, false())]" + } + }, + { + "name": "fabricSettingsVar", + "count": "[length(parameters('fabricSettings'))]", + "input": { + "name": "[if(contains(parameters('fabricSettings')[copyIndex('fabricSettingsVar')], 'name'), parameters('fabricSettings')[copyIndex('fabricSettingsVar')].name, null())]", + "parameters": "[if(contains(parameters('fabricSettings')[copyIndex('fabricSettingsVar')], 'parameters'), parameters('fabricSettings')[copyIndex('fabricSettingsVar')].parameters, null())]" + } + }, + { + "name": "fnodeTypesVar", + "count": "[length(parameters('nodeTypes'))]", + "input": { + "applicationPorts": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'applicationPorts'), createObject('endPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts, 'endPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts.endPort, null()), 'startPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts, 'startPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].applicationPorts.startPort, null())), null())]", + "capacities": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'capacities'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].capacities, null())]", + "clientConnectionEndpointPort": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'clientConnectionEndpointPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].clientConnectionEndpointPort, null())]", + "durabilityLevel": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'durabilityLevel'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].durabilityLevel, null())]", + "ephemeralPorts": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'ephemeralPorts'), createObject('endPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts, 'endPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts.endPort, null()), 'startPort', if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts, 'startPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].ephemeralPorts.startPort, null())), null())]", + "httpGatewayEndpointPort": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'httpGatewayEndpointPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].httpGatewayEndpointPort, null())]", + "isPrimary": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'isPrimary'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].isPrimary, null())]", + "isStateless": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'isStateless'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].isStateless, null())]", + "multipleAvailabilityZones": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'multipleAvailabilityZones'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].multipleAvailabilityZones, null())]", + "name": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'name'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].name, 'Node00')]", + "placementProperties": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'placementProperties'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].placementProperties, null())]", + "reverseProxyEndpointPort": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'reverseProxyEndpointPort'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].reverseProxyEndpointPort, null())]", + "vmInstanceCount": "[if(contains(parameters('nodeTypes')[copyIndex('fnodeTypesVar')], 'vmInstanceCount'), parameters('nodeTypes')[copyIndex('fnodeTypesVar')].vmInstanceCount, 1)]" + } + }, + { + "name": "notificationsVar", + "count": "[length(parameters('notifications'))]", + "input": { + "isEnabled": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'isEnabled'), parameters('notifications')[copyIndex('notificationsVar')].isEnabled, false())]", + "notificationCategory": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationCategory'), parameters('notifications')[copyIndex('notificationsVar')].notificationCategory, 'WaveProgress')]", + "notificationLevel": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationLevel'), parameters('notifications')[copyIndex('notificationsVar')].notificationLevel, 'All')]", + "notificationTargets": "[if(contains(parameters('notifications')[copyIndex('notificationsVar')], 'notificationTargets'), parameters('notifications')[copyIndex('notificationsVar')].notificationTargets, createArray())]" + } + } + ], + "upgradeDescriptionVar": "[union(createObject('deltaHealthPolicy', createObject('applicationDeltaHealthPolicies', coalesce(tryGet(parameters('upgradeDescription'), 'applicationDeltaHealthPolicies'), createObject()), 'maxPercentDeltaUnhealthyApplications', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentDeltaUnhealthyApplications'), 0), 'maxPercentDeltaUnhealthyNodes', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentDeltaUnhealthyNodes'), 0), 'maxPercentUpgradeDomainDeltaUnhealthyNodes', coalesce(tryGet(parameters('upgradeDescription'), 'maxPercentUpgradeDomainDeltaUnhealthyNodes'), 0)), 'forceRestart', if(contains(parameters('upgradeDescription'), 'forceRestart'), parameters('upgradeDescription').forceRestart, false()), 'healthCheckRetryTimeout', if(contains(parameters('upgradeDescription'), 'healthCheckRetryTimeout'), parameters('upgradeDescription').healthCheckRetryTimeout, '00:45:00'), 'healthCheckStableDuration', if(contains(parameters('upgradeDescription'), 'healthCheckStableDuration'), parameters('upgradeDescription').healthCheckStableDuration, '00:01:00'), 'healthCheckWaitDuration', if(contains(parameters('upgradeDescription'), 'healthCheckWaitDuration'), parameters('upgradeDescription').healthCheckWaitDuration, '00:00:30'), 'upgradeDomainTimeout', if(contains(parameters('upgradeDescription'), 'upgradeDomainTimeout'), parameters('upgradeDescription').upgradeDomainTimeout, '02:00:00'), 'upgradeReplicaSetCheckTimeout', if(contains(parameters('upgradeDescription'), 'upgradeReplicaSetCheckTimeout'), parameters('upgradeDescription').upgradeReplicaSetCheckTimeout, '1.00:00:00'), 'upgradeTimeout', if(contains(parameters('upgradeDescription'), 'upgradeTimeout'), parameters('upgradeDescription').upgradeTimeout, '02:00:00')), if(contains(parameters('upgradeDescription'), 'healthPolicy'), createObject('healthPolicy', createObject('applicationHealthPolicies', if(contains(parameters('upgradeDescription').healthPolicy, 'applicationHealthPolicies'), parameters('upgradeDescription').healthPolicy.applicationHealthPolicies, createObject()), 'maxPercentUnhealthyApplications', if(contains(parameters('upgradeDescription').healthPolicy, 'maxPercentUnhealthyApplications'), parameters('upgradeDescription').healthPolicy.maxPercentUnhealthyApplications, 0), 'maxPercentUnhealthyNodes', if(contains(parameters('upgradeDescription').healthPolicy, 'maxPercentUnhealthyNodes'), parameters('upgradeDescription').healthPolicy.maxPercentUnhealthyNodes, 0))), createObject()))]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.servicefabric-cluster.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "serviceFabricCluster": { + "type": "Microsoft.ServiceFabric/clusters", + "apiVersion": "2021-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addOnFeatures": "[parameters('addOnFeatures')]", + "applicationTypeVersionsCleanupPolicy": { + "maxUnusedVersionsToKeep": "[parameters('maxUnusedVersionsToKeep')]" + }, + "azureActiveDirectory": "[if(not(empty(parameters('azureActiveDirectory'))), createObject('clientApplication', if(contains(parameters('azureActiveDirectory'), 'clientApplication'), parameters('azureActiveDirectory').clientApplication, null()), 'clusterApplication', if(contains(parameters('azureActiveDirectory'), 'clusterApplication'), parameters('azureActiveDirectory').clusterApplication, null()), 'tenantId', if(contains(parameters('azureActiveDirectory'), 'tenantId'), parameters('azureActiveDirectory').tenantId, null())), null())]", + "certificate": "[if(not(empty(parameters('certificate'))), createObject('thumbprint', if(contains(parameters('certificate'), 'thumbprint'), parameters('certificate').thumbprint, null()), 'thumbprintSecondary', if(contains(parameters('certificate'), 'thumbprintSecondary'), parameters('certificate').thumbprintSecondary, null()), 'x509StoreName', if(contains(parameters('certificate'), 'x509StoreName'), parameters('certificate').x509StoreName, null())), null())]", + "certificateCommonNames": "[if(not(empty(parameters('certificateCommonNames'))), createObject('commonNames', if(contains(parameters('certificateCommonNames'), 'commonNames'), parameters('certificateCommonNames').commonNames, null()), 'x509StoreName', if(contains(parameters('certificateCommonNames'), 'certificateCommonNamesx509StoreName'), parameters('certificateCommonNames').certificateCommonNamesx509StoreName, null())), null())]", + "clientCertificateCommonNames": "[if(not(empty(parameters('clientCertificateCommonNames'))), variables('clientCertificateCommonNamesVar'), null())]", + "clientCertificateThumbprints": "[if(not(empty(parameters('clientCertificateThumbprints'))), variables('clientCertificateThumbprintsVar'), null())]", + "clusterCodeVersion": "[parameters('clusterCodeVersion')]", + "diagnosticsStorageAccountConfig": "[if(not(empty(parameters('diagnosticsStorageAccountConfig'))), createObject('blobEndpoint', if(contains(parameters('diagnosticsStorageAccountConfig'), 'blobEndpoint'), parameters('diagnosticsStorageAccountConfig').blobEndpoint, null()), 'protectedAccountKeyName', if(contains(parameters('diagnosticsStorageAccountConfig'), 'protectedAccountKeyName'), parameters('diagnosticsStorageAccountConfig').protectedAccountKeyName, null()), 'protectedAccountKeyName2', if(contains(parameters('diagnosticsStorageAccountConfig'), 'protectedAccountKeyName2'), parameters('diagnosticsStorageAccountConfig').protectedAccountKeyName2, null()), 'queueEndpoint', if(contains(parameters('diagnosticsStorageAccountConfig'), 'queueEndpoint'), parameters('diagnosticsStorageAccountConfig').queueEndpoint, null()), 'storageAccountName', if(contains(parameters('diagnosticsStorageAccountConfig'), 'storageAccountName'), parameters('diagnosticsStorageAccountConfig').storageAccountName, null()), 'tableEndpoint', if(contains(parameters('diagnosticsStorageAccountConfig'), 'tableEndpoint'), parameters('diagnosticsStorageAccountConfig').tableEndpoint, null())), null())]", + "eventStoreServiceEnabled": "[parameters('eventStoreServiceEnabled')]", + "fabricSettings": "[if(not(empty(parameters('fabricSettings'))), variables('fabricSettingsVar'), null())]", + "infrastructureServiceManager": "[parameters('infrastructureServiceManager')]", + "managementEndpoint": "[parameters('managementEndpoint')]", + "nodeTypes": "[if(not(empty(parameters('nodeTypes'))), variables('fnodeTypesVar'), createArray())]", + "notifications": "[if(not(empty(parameters('notifications'))), variables('notificationsVar'), null())]", + "reliabilityLevel": "[if(not(empty(parameters('reliabilityLevel'))), parameters('reliabilityLevel'), 'None')]", + "reverseProxyCertificate": "[if(not(empty(parameters('reverseProxyCertificate'))), createObject('thumbprint', if(contains(parameters('reverseProxyCertificate'), 'thumbprint'), parameters('reverseProxyCertificate').thumbprint, null()), 'thumbprintSecondary', if(contains(parameters('reverseProxyCertificate'), 'thumbprintSecondary'), parameters('reverseProxyCertificate').thumbprintSecondary, null()), 'x509StoreName', if(contains(parameters('reverseProxyCertificate'), 'x509StoreName'), parameters('reverseProxyCertificate').x509StoreName, null())), null())]", + "reverseProxyCertificateCommonNames": "[if(not(empty(parameters('reverseProxyCertificateCommonNames'))), createObject('commonNames', if(contains(parameters('reverseProxyCertificateCommonNames'), 'commonNames'), parameters('reverseProxyCertificateCommonNames').commonNames, null()), 'x509StoreName', if(contains(parameters('reverseProxyCertificateCommonNames'), 'x509StoreName'), parameters('reverseProxyCertificateCommonNames').x509StoreName, null())), null())]", + "sfZonalUpgradeMode": "[if(not(empty(parameters('sfZonalUpgradeMode'))), parameters('sfZonalUpgradeMode'), null())]", + "upgradeDescription": "[if(not(empty(parameters('upgradeDescription'))), variables('upgradeDescriptionVar'), null())]", + "upgradeMode": "[if(not(empty(parameters('upgradeMode'))), parameters('upgradeMode'), null())]", + "upgradePauseEndTimestampUtc": "[parameters('upgradePauseEndTimestampUtc')]", + "upgradePauseStartTimestampUtc": "[parameters('upgradePauseStartTimestampUtc')]", + "upgradeWave": "[if(not(empty(parameters('upgradeWave'))), parameters('upgradeWave'), null())]", + "vmImage": "[parameters('vmImage')]", + "vmssZonalUpgradeMode": "[if(not(empty(parameters('vmssZonalUpgradeMode'))), parameters('vmssZonalUpgradeMode'), null())]", + "waveUpgradePaused": "[parameters('waveUpgradePaused')]" + } + }, + "serviceFabricCluster_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ServiceFabric/clusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "serviceFabricCluster" + ] + }, + "serviceFabricCluster_roleAssignments": { + "copy": { + "name": "serviceFabricCluster_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ServiceFabric/clusters/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ServiceFabric/clusters', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "serviceFabricCluster" + ] + }, + "serviceFabricCluster_applicationTypes": { + "copy": { + "name": "serviceFabricCluster_applicationTypes", + "count": "[length(parameters('applicationTypes'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SFC-{1}', uniqueString(deployment().name, parameters('location')), parameters('applicationTypes')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('applicationTypes')[copyIndex()].name]" + }, + "serviceFabricClusterName": { + "value": "[parameters('name')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('applicationTypes')[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4241007545422374207" + }, + "name": "Service Fabric Cluster Application Types", + "description": "This module deploys a Service Fabric Cluster Application Type.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "serviceFabricClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Service Fabric cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "defaultApplicationType", + "metadata": { + "description": "Optional. Application type name." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "serviceFabricCluster": { + "existing": true, + "type": "Microsoft.ServiceFabric/clusters", + "apiVersion": "2021-06-01", + "name": "[parameters('serviceFabricClusterName')]" + }, + "applicationTypes": { + "type": "Microsoft.ServiceFabric/clusters/applicationTypes", + "apiVersion": "2021-06-01", + "name": "[format('{0}/{1}', parameters('serviceFabricClusterName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "dependsOn": [ + "serviceFabricCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The resource name of the Application type." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the Application type." + }, + "value": "[resourceGroup().name]" + }, + "resourceID": { + "type": "string", + "metadata": { + "description": "The resource ID of the Application type." + }, + "value": "[resourceId('Microsoft.ServiceFabric/clusters/applicationTypes', parameters('serviceFabricClusterName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "serviceFabricCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster name." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster resource group." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster resource ID." + }, + "value": "[resourceId('Microsoft.ServiceFabric/clusters', parameters('name'))]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The Service Fabric Cluster endpoint." + }, + "value": "[reference('serviceFabricCluster').clusterEndpoint]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('serviceFabricCluster', '2021-06-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep new file mode 100644 index 0000000000..3b291fce05 --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/cert/main.test.bicep @@ -0,0 +1,71 @@ +targetScope = 'subscription' + +metadata name = 'Certificate' +metadata description = 'This instance deploys the module with a certificate.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfccer' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'None' + certificate: { + thumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + x509StoreName: 'My' + } + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..37e0d4b9c5 --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,67 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfcmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'None' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep b/avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..edbdc3dded --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/max/dependencies.bicep @@ -0,0 +1,31 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the storage account to create.') +param storageAccountName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name diff --git a/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..8681e32333 --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/max/main.test.bicep @@ -0,0 +1,238 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfcmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + clusterName: '${namePrefix}${serviceShort}001' + } + addOnFeatures: [ + 'RepairManager' + 'DnsService' + 'BackupRestoreService' + 'ResourceMonitorService' + ] + maxUnusedVersionsToKeep: 2 + azureActiveDirectory: { + clientApplication: nestedDependencies.outputs.managedIdentityPrincipalId + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: tenant().tenantId + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}/' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.queue.${environment().suffixes.storage}/' + storageAccountName: nestedDependencies.outputs.storageAccountName + tableEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.table.${environment().suffixes.storage}/' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'Silver' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + + isStateless: false + multipleAvailabilityZones: false + + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + startPort: 49000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + vmInstanceCount: 5 + } + } + ] + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + upgradeDescription: { + forceRestart: false + upgradeReplicaSetCheckTimeout: '1.00:00:00' + healthCheckWaitDuration: '00:00:30' + healthCheckStableDuration: '00:01:00' + healthCheckRetryTimeout: '00:45:00' + upgradeTimeout: '02:00:00' + upgradeDomainTimeout: '02:00:00' + healthPolicy: { + maxPercentUnhealthyNodes: 0 + maxPercentUnhealthyApplications: 0 + } + deltaHealthPolicy: { + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + maxPercentDeltaUnhealthyApplications: 0 + } + } + vmImage: 'Linux' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + applicationTypes: [ + { + name: 'WordCount' + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..edbdc3dded --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,31 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the storage account to create.') +param storageAccountName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The name of the created Storage Account.') +output storageAccountName string = storageAccount.name diff --git a/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..cee548743e --- /dev/null +++ b/avm/res/service-fabric/cluster/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,214 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicefabric.clusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sfcwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + storageAccountName: 'dep${namePrefix}azsa${serviceShort}01' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + tags: { + 'hidden-title': 'This is visible in the resource name' + resourceType: 'Service Fabric' + clusterName: '${namePrefix}${serviceShort}001' + } + addOnFeatures: [ + 'RepairManager' + 'DnsService' + 'BackupRestoreService' + 'ResourceMonitorService' + ] + maxUnusedVersionsToKeep: 2 + azureActiveDirectory: { + clientApplication: nestedDependencies.outputs.managedIdentityPrincipalId + clusterApplication: 'cf33fea8-b30f-424f-ab73-c48d99e0b222' + tenantId: tenant().tenantId + } + certificateCommonNames: { + commonNames: [ + { + certificateCommonName: 'certcommon' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + } + ] + x509StoreName: '' + } + clientCertificateCommonNames: [ + { + certificateCommonName: 'clientcommoncert1' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateCommonName: 'clientcommoncert2' + certificateIssuerThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + clientCertificateThumbprints: [ + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC130' + isAdmin: false + } + { + certificateThumbprint: '0AC113D5E1D94C401DDEB0EE2B1B96CC131' + isAdmin: false + } + ] + diagnosticsStorageAccountConfig: { + blobEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.blob.${environment().suffixes.storage}/' + protectedAccountKeyName: 'StorageAccountKey1' + queueEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.queue.${environment().suffixes.storage}/' + storageAccountName: nestedDependencies.outputs.storageAccountName + tableEndpoint: 'https://${nestedDependencies.outputs.storageAccountName}.table.${environment().suffixes.storage}/' + } + fabricSettings: [ + { + name: 'Security' + parameters: [ + { + name: 'ClusterProtectionLevel' + value: 'EncryptAndSign' + } + ] + } + { + name: 'UpgradeService' + parameters: [ + { + name: 'AppPollIntervalInSeconds' + value: '60' + } + ] + } + ] + managementEndpoint: 'https://${namePrefix}${serviceShort}001.westeurope.cloudapp.azure.com:19080' + reliabilityLevel: 'Silver' + nodeTypes: [ + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Silver' + ephemeralPorts: { + endPort: 65534 + startPort: 49152 + } + httpGatewayEndpointPort: 19080 + isPrimary: true + name: 'Node01' + + isStateless: false + multipleAvailabilityZones: false + + placementProperties: {} + reverseProxyEndpointPort: '' + vmInstanceCount: 5 + } + { + applicationPorts: { + endPort: 30000 + startPort: 20000 + } + clientConnectionEndpointPort: 19000 + durabilityLevel: 'Bronze' + ephemeralPorts: { + endPort: 64000 + startPort: 49000 + httpGatewayEndpointPort: 19007 + isPrimary: true + name: 'Node02' + vmInstanceCount: 5 + } + } + ] + notifications: [ + { + isEnabled: true + notificationCategory: 'WaveProgress' + notificationLevel: 'Critical' + notificationTargets: [ + { + notificationChannel: 'EmailUser' + receivers: [ + 'SomeReceiver' + ] + } + ] + } + ] + upgradeDescription: { + forceRestart: false + upgradeReplicaSetCheckTimeout: '1.00:00:00' + healthCheckWaitDuration: '00:00:30' + healthCheckStableDuration: '00:01:00' + healthCheckRetryTimeout: '00:45:00' + upgradeTimeout: '02:00:00' + upgradeDomainTimeout: '02:00:00' + healthPolicy: { + maxPercentUnhealthyNodes: 0 + maxPercentUnhealthyApplications: 0 + } + deltaHealthPolicy: { + maxPercentDeltaUnhealthyNodes: 0 + maxPercentUpgradeDomainDeltaUnhealthyNodes: 0 + maxPercentDeltaUnhealthyApplications: 0 + } + } + vmImage: 'Linux' + applicationTypes: [ + { + name: 'WordCount' // not idempotent + } + ] + } + } +] diff --git a/avm/res/service-fabric/cluster/version.json b/avm/res/service-fabric/cluster/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/service-fabric/cluster/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 841a1f13c3b3956effa17056bd86aa417ea39078 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Wed, 10 Apr 2024 22:06:21 +0200 Subject: [PATCH 17/66] fix: Ported additional fixes for Path-Publish action to Tag-Publish workflow (#1637) ## Description - Ported additional fixes for Path-Publish action to Tag-Publish workflow - Additional output - Correct function reference ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- .github/workflows/avm.platform.publish-tag.yml | 2 +- avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/avm.platform.publish-tag.yml b/.github/workflows/avm.platform.publish-tag.yml index 702fe493f2..194531cad2 100644 --- a/.github/workflows/avm.platform.publish-tag.yml +++ b/.github/workflows/avm.platform.publish-tag.yml @@ -63,7 +63,7 @@ jobs: Write-Verbose 'Invoke function with' -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose - if($publishOutputs = Publish-ModuleFromPathToPBR @functionInput -Verbose) { + if($publishOutputs = Publish-ModuleFromTagToPBR @functionInput -Verbose) { $publishOutputs.Keys | Foreach-Object { Write-Verbose ('Passing pipeline variable [{0}] with value [{1}]' -f $_, $publishOutputs.$_) -Verbose Write-Output ('{0}={1}' -f $_, $publishOutputs.$_) >> $env:GITHUB_OUTPUT diff --git a/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 b/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 index 8d15039f1a..9e92d60d76 100644 --- a/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 +++ b/avm/utilities/pipelines/platform/Publish-ModuleFromTagToPBR.ps1 @@ -94,5 +94,6 @@ function Publish-ModuleFromTagToPBR { return @{ version = $targetVersion publishedModuleName = $moduleRelativeFolderPath + gitTagName = $ModuleReleaseTagName } } From c4e873520542ae2d134684f9ec9707c30d17922a Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Thu, 11 Apr 2024 06:27:47 +0100 Subject: [PATCH 18/66] fix: Platform scripts trigger exclusion missing in a few module validation workflows (#1643) ## Description Platform scripts trigger exclusion missing in a few module validation workflows ## Pipeline Reference | Pipeline | | -------- | | N/A | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/workflows/avm.res.aad.domain-service.yml | 1 + .github/workflows/avm.res.container-instance.container-group.yml | 1 + .../workflows/avm.res.digital-twins.digital-twins-instance.yml | 1 + .github/workflows/avm.res.network.service-endpoint-policy.yml | 1 + .github/workflows/avm.res.recovery-services.vault.yml | 1 + .github/workflows/avm.res.service-fabric.cluster.yml | 1 + 6 files changed, 6 insertions(+) diff --git a/.github/workflows/avm.res.aad.domain-service.yml b/.github/workflows/avm.res.aad.domain-service.yml index b5eb8d5e6a..b6c0a5883f 100644 --- a/.github/workflows/avm.res.aad.domain-service.yml +++ b/.github/workflows/avm.res.aad.domain-service.yml @@ -29,6 +29,7 @@ on: - ".github/workflows/avm.res.aad.domain-service.yml" - "avm/res/aad/domain-service/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: diff --git a/.github/workflows/avm.res.container-instance.container-group.yml b/.github/workflows/avm.res.container-instance.container-group.yml index 5f40c3cc22..9782de00fa 100644 --- a/.github/workflows/avm.res.container-instance.container-group.yml +++ b/.github/workflows/avm.res.container-instance.container-group.yml @@ -29,6 +29,7 @@ on: - ".github/workflows/avm.res.container-instance.container-group.yml" - "avm/res/container-instance/container-group/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: diff --git a/.github/workflows/avm.res.digital-twins.digital-twins-instance.yml b/.github/workflows/avm.res.digital-twins.digital-twins-instance.yml index 71b6b56d1a..fc9e6fb5da 100644 --- a/.github/workflows/avm.res.digital-twins.digital-twins-instance.yml +++ b/.github/workflows/avm.res.digital-twins.digital-twins-instance.yml @@ -29,6 +29,7 @@ on: - ".github/workflows/avm.res.digital-twins.digital-twins-instance.yml" - "avm/res/digital-twins/digital-twins-instance/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: diff --git a/.github/workflows/avm.res.network.service-endpoint-policy.yml b/.github/workflows/avm.res.network.service-endpoint-policy.yml index def975dfe0..80e26edeac 100644 --- a/.github/workflows/avm.res.network.service-endpoint-policy.yml +++ b/.github/workflows/avm.res.network.service-endpoint-policy.yml @@ -29,6 +29,7 @@ on: - ".github/workflows/avm.res.network.service-endpoint-policy.yml" - "avm/res/network/service-endpoint-policy/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: diff --git a/.github/workflows/avm.res.recovery-services.vault.yml b/.github/workflows/avm.res.recovery-services.vault.yml index bc630cebbc..9d5a1dc1ff 100644 --- a/.github/workflows/avm.res.recovery-services.vault.yml +++ b/.github/workflows/avm.res.recovery-services.vault.yml @@ -29,6 +29,7 @@ on: - ".github/workflows/avm.res.recovery-services.vault.yml" - "avm/res/recovery-services/vault/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: diff --git a/.github/workflows/avm.res.service-fabric.cluster.yml b/.github/workflows/avm.res.service-fabric.cluster.yml index 93c11bbf84..2e2c93206c 100644 --- a/.github/workflows/avm.res.service-fabric.cluster.yml +++ b/.github/workflows/avm.res.service-fabric.cluster.yml @@ -29,6 +29,7 @@ on: - ".github/workflows/avm.res.service-fabric.cluster.yml" - "avm/res/service-fabric/cluster/**" - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: From afeaf3eafef3d031590bcb3dd509ad57d171e8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenjie=20Yu=EF=BC=88MSFT=EF=BC=89?= <81678720+zedy-wj@users.noreply.github.com> Date: Thu, 11 Apr 2024 15:00:55 +0800 Subject: [PATCH 19/66] feat: Add output 'kubeletidentityClientId' and 'kubeletidentityResourceId' in AKS Module - `avm/res/container-service/managed-cluster` (#1464) Fixed issue #1128. Updated content: - Add output `kubeletidentityClientId` and `kubeletidentityResourceId` in AKS Module. - Update readme file in AKS Module. @jongio for notification. --------- Co-authored-by: zedy Co-authored-by: Alexander Sehr --- .../container-service/managed-cluster/README.md | 4 +++- .../container-service/managed-cluster/main.bicep | 8 +++++++- .../container-service/managed-cluster/main.json | 16 +++++++++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index ec4fc5f535..9c6755ddb5 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -3233,7 +3233,9 @@ Specifies whether the webApplicationRoutingEnabled add-on is enabled or not. | `ingressApplicationGatewayIdentityObjectId` | string | The Object ID of Application Gateway Ingress Controller (AGIC) identity. | | `keyvaultIdentityClientId` | string | The Client ID of the Key Vault Secrets Provider identity. | | `keyvaultIdentityObjectId` | string | The Object ID of the Key Vault Secrets Provider identity. | -| `kubeletidentityObjectId` | string | The Object ID of the AKS identity. | +| `kubeletIdentityClientId` | string | The Client ID of the AKS identity. | +| `kubeletIdentityObjectId` | string | The Object ID of the AKS identity. | +| `kubeletIdentityResourceId` | string | The Resource ID of the AKS identity. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the managed cluster. | | `oidcIssuerUrl` | string | The OIDC token issuer URL. | diff --git a/avm/res/container-service/managed-cluster/main.bicep b/avm/res/container-service/managed-cluster/main.bicep index 492fb55b86..d8ffe6bc8f 100644 --- a/avm/res/container-service/managed-cluster/main.bicep +++ b/avm/res/container-service/managed-cluster/main.bicep @@ -892,8 +892,14 @@ output controlPlaneFQDN string = enablePrivateCluster @description('The principal ID of the system assigned identity.') output systemAssignedMIPrincipalId string = managedCluster.?identity.?principalId ?? '' +@description('The Client ID of the AKS identity.') +output kubeletIdentityClientId string = managedCluster.properties.?identityProfile.?kubeletidentity.?clientId ?? '' + @description('The Object ID of the AKS identity.') -output kubeletidentityObjectId string = managedCluster.properties.?identityProfile.?kubeletidentity.?objectId ?? '' +output kubeletIdentityObjectId string = managedCluster.properties.?identityProfile.?kubeletidentity.?objectId ?? '' + +@description('The Resource ID of the AKS identity.') +output kubeletIdentityResourceId string = managedCluster.properties.?identityProfile.?kubeletidentity.?resourceId ?? '' @description('The Object ID of the OMS agent identity.') output omsagentIdentityObjectId string = managedCluster.properties.?addonProfiles.?omsagent.?identity.?objectId ?? '' diff --git a/avm/res/container-service/managed-cluster/main.json b/avm/res/container-service/managed-cluster/main.json index e1f4ab5d28..b8fc5076be 100644 --- a/avm/res/container-service/managed-cluster/main.json +++ b/avm/res/container-service/managed-cluster/main.json @@ -2736,13 +2736,27 @@ }, "value": "[coalesce(tryGet(tryGet(reference('managedCluster', '2023-07-02-preview', 'full'), 'identity'), 'principalId'), '')]" }, - "kubeletidentityObjectId": { + "kubeletIdentityClientId": { + "type": "string", + "metadata": { + "description": "The Client ID of the AKS identity." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(reference('managedCluster'), 'identityProfile'), 'kubeletidentity'), 'clientId'), '')]" + }, + "kubeletIdentityObjectId": { "type": "string", "metadata": { "description": "The Object ID of the AKS identity." }, "value": "[coalesce(tryGet(tryGet(tryGet(reference('managedCluster'), 'identityProfile'), 'kubeletidentity'), 'objectId'), '')]" }, + "kubeletIdentityResourceId": { + "type": "string", + "metadata": { + "description": "The Resource ID of the AKS identity." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(reference('managedCluster'), 'identityProfile'), 'kubeletidentity'), 'resourceId'), '')]" + }, "omsagentIdentityObjectId": { "type": "string", "metadata": { From 80175c7cb472658bb0e3599afab4f331f603f11e Mon Sep 17 00:00:00 2001 From: Pierre Malarme Date: Thu, 11 Apr 2024 10:08:23 +0200 Subject: [PATCH 20/66] fix: Fix virtual machine warnings for max price of the billing profile and the type of the identity - `avm/res/compute/virtual-machine` (#1617) ## Description This PR fixes 2 Bicep warning BCP036: * Missing space between `SystemAssigned` and `UserAssigned` for the type of the identity * The value of `maxPrice` for the billing profile needs to be an integer or a json decimal: https://learn.microsoft.com/en-us/azure/templates/microsoft.compute/2022-11-01/virtualmachines?pivots=deployment-language-bicep#billingprofile. By using `json()` function, I ensure that this value is converted to `int` or json decimal. ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com> --- avm/res/compute/virtual-machine/main.bicep | 4 ++-- avm/res/compute/virtual-machine/version.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/avm/res/compute/virtual-machine/main.bicep b/avm/res/compute/virtual-machine/main.bicep index a15b26418c..1fe7b22def 100644 --- a/avm/res/compute/virtual-machine/main.bicep +++ b/avm/res/compute/virtual-machine/main.bicep @@ -330,7 +330,7 @@ var formattedUserAssignedIdentities = reduce( var identity = !empty(managedIdentities) ? { type: (extensionAadJoinConfig.enabled ? true : (managedIdentities.?systemAssigned ?? false)) - ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') + ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned') : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null) userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null } @@ -554,7 +554,7 @@ resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = { evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null billingProfile: !empty(priority) && !empty(maxPriceForLowPriorityVm) ? { - maxPrice: maxPriceForLowPriorityVm + maxPrice: json(maxPriceForLowPriorityVm) } : null host: !empty(dedicatedHostId) diff --git a/avm/res/compute/virtual-machine/version.json b/avm/res/compute/virtual-machine/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/compute/virtual-machine/version.json +++ b/avm/res/compute/virtual-machine/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] From b71115c2d46c3e01241a62e0d0ebb40632b546bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Gr=C3=A4f?= Date: Thu, 11 Apr 2024 18:47:04 +1000 Subject: [PATCH 21/66] feat: module `avm/res/network/network-watcher` (#1478) ## Description Closes https://github.com/Azure/Azure-Verified-Modules/issues/581 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.network-watcher](https://github.com/segraef/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml/badge.svg?branch=nw)](https://github.com/segraef/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Alexander Sehr Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.network.network-watcher.yml | 87 +++ avm/res/network/network-watcher/README.md | 712 ++++++++++++++++++ .../connection-monitor/README.md | 117 +++ .../connection-monitor/main.bicep | 66 ++ .../connection-monitor/main.json | 126 ++++ .../network-watcher/flow-log/README.md | 158 ++++ .../network-watcher/flow-log/main.bicep | 97 +++ .../network-watcher/flow-log/main.json | 167 ++++ avm/res/network/network-watcher/main.bicep | 188 +++++ avm/res/network/network-watcher/main.json | 633 ++++++++++++++++ .../tests/e2e/defaults/main.test.bicep | 51 ++ .../tests/e2e/max/dependencies.bicep | 144 ++++ .../tests/e2e/max/main.test.bicep | 172 +++++ .../tests/e2e/waf-aligned/dependencies.bicep | 144 ++++ .../tests/e2e/waf-aligned/main.test.bicep | 156 ++++ avm/res/network/network-watcher/version.json | 7 + 18 files changed, 3027 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.network.network-watcher.yml create mode 100644 avm/res/network/network-watcher/README.md create mode 100644 avm/res/network/network-watcher/connection-monitor/README.md create mode 100644 avm/res/network/network-watcher/connection-monitor/main.bicep create mode 100644 avm/res/network/network-watcher/connection-monitor/main.json create mode 100644 avm/res/network/network-watcher/flow-log/README.md create mode 100644 avm/res/network/network-watcher/flow-log/main.bicep create mode 100644 avm/res/network/network-watcher/flow-log/main.json create mode 100644 avm/res/network/network-watcher/main.bicep create mode 100644 avm/res/network/network-watcher/main.json create mode 100644 avm/res/network/network-watcher/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/network/network-watcher/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/network/network-watcher/tests/e2e/max/main.test.bicep create mode 100644 avm/res/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/network/network-watcher/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aeceb727dd..dd7d4654fc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -98,7 +98,7 @@ /avm/res/network/network-interface/ @Azure/avm-res-network-networkinterface-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/network-manager/ @Azure/avm-res-network-networkmanager-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/network-security-group/ @Azure/avm-res-network-networksecuritygroup-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/network/network-watcher/ @Azure/avm-res-network-networkwatcher-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/network/network-watcher/ @Azure/avm-res-network-networkwatcher-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/private-dns-zone/ @Azure/avm-res-network-privatednszone-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/private-endpoint/ @Azure/avm-res-network-privateendpoint-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/private-link-service/ @Azure/avm-res-network-privatelinkservice-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index a1f30bffbc..9fea2a0a2e 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -131,7 +131,7 @@ body: - "avm/res/network/network-interface" - "avm/res/network/network-manager" - "avm/res/network/network-security-group" - # - "avm/res/network/network-watcher" + - "avm/res/network/network-watcher" - "avm/res/network/private-dns-zone" - "avm/res/network/private-endpoint" - "avm/res/network/private-link-service" diff --git a/.github/workflows/avm.res.network.network-watcher.yml b/.github/workflows/avm.res.network.network-watcher.yml new file mode 100644 index 0000000000..823b4baf3e --- /dev/null +++ b/.github/workflows/avm.res.network.network-watcher.yml @@ -0,0 +1,87 @@ +name: "avm.res.network.network-watcher" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.network.network-watcher.yml" + - "avm/res/network/network-watcher/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/network/network-watcher" + workflowPath: ".github/workflows/avm.res.network.network-watcher.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/network/network-watcher/README.md b/avm/res/network/network-watcher/README.md new file mode 100644 index 0000000000..b415feec56 --- /dev/null +++ b/avm/res/network/network-watcher/README.md @@ -0,0 +1,712 @@ +# Network Watchers `[Microsoft.Network/networkWatchers]` + +This module deploys a Network Watcher. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Network/networkWatchers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkWatchers) | +| `Microsoft.Network/networkWatchers/connectionMonitors` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkWatchers/connectionMonitors) | +| `Microsoft.Network/networkWatchers/flowLogs` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkWatchers/flowLogs) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/network-watcher:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module networkWatcher 'br/public:avm/res/network/network-watcher:' = { + name: 'networkWatcherDeployment' + params: { + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module networkWatcher 'br/public:avm/res/network/network-watcher:' = { + name: 'networkWatcherDeployment' + params: { + connectionMonitors: [ + { + endpoints: [ + { + name: '' + resourceId: '' + type: 'AzureVM' + } + { + address: 'www.bing.com' + name: 'Bing' + type: 'ExternalAddress' + } + ] + name: 'nnwmax-cm-001' + testConfigurations: [ + { + httpConfiguration: { + method: 'Get' + port: 80 + preferHTTPS: false + requestHeaders: [] + validStatusCodeRanges: [ + '200' + ] + } + name: 'HTTP Bing Test' + protocol: 'Http' + successThreshold: { + checksFailedPercent: 5 + roundTripTimeMs: 100 + } + testFrequencySec: 30 + } + ] + testGroups: [ + { + destinations: [ + 'Bing' + ] + disable: false + name: 'test-http-Bing' + sources: [ + 'subnet-001(${resourceGroup.name})' + ] + testConfigurations: [ + 'HTTP Bing Test' + ] + } + ] + workspaceResourceId: '' + } + ] + flowLogs: [ + { + enabled: false + storageId: '' + targetResourceId: '' + } + { + formatVersion: 1 + name: 'nnwmax-fl-001' + retentionInDays: 8 + storageId: '' + targetResourceId: '' + trafficAnalyticsInterval: 10 + workspaceResourceId: '' + } + ] + location: '' + name: '' + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "connectionMonitors": { + "value": [ + { + "endpoints": [ + { + "name": "", + "resourceId": "", + "type": "AzureVM" + }, + { + "address": "www.bing.com", + "name": "Bing", + "type": "ExternalAddress" + } + ], + "name": "nnwmax-cm-001", + "testConfigurations": [ + { + "httpConfiguration": { + "method": "Get", + "port": 80, + "preferHTTPS": false, + "requestHeaders": [], + "validStatusCodeRanges": [ + "200" + ] + }, + "name": "HTTP Bing Test", + "protocol": "Http", + "successThreshold": { + "checksFailedPercent": 5, + "roundTripTimeMs": 100 + }, + "testFrequencySec": 30 + } + ], + "testGroups": [ + { + "destinations": [ + "Bing" + ], + "disable": false, + "name": "test-http-Bing", + "sources": [ + "subnet-001(${resourceGroup.name})" + ], + "testConfigurations": [ + "HTTP Bing Test" + ] + } + ], + "workspaceResourceId": "" + } + ] + }, + "flowLogs": { + "value": [ + { + "enabled": false, + "storageId": "", + "targetResourceId": "" + }, + { + "formatVersion": 1, + "name": "nnwmax-fl-001", + "retentionInDays": 8, + "storageId": "", + "targetResourceId": "", + "trafficAnalyticsInterval": 10, + "workspaceResourceId": "" + } + ] + }, + "location": { + "value": "" + }, + "name": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +### Example 3: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module networkWatcher 'br/public:avm/res/network/network-watcher:' = { + name: 'networkWatcherDeployment' + params: { + connectionMonitors: [ + { + endpoints: [ + { + name: '' + resourceId: '' + type: 'AzureVM' + } + { + address: 'www.bing.com' + name: 'Bing' + type: 'ExternalAddress' + } + ] + name: 'nnwwaf-cm-001' + testConfigurations: [ + { + httpConfiguration: { + method: 'Get' + port: 80 + preferHTTPS: false + requestHeaders: [] + validStatusCodeRanges: [ + '200' + ] + } + name: 'HTTP Bing Test' + protocol: 'Http' + successThreshold: { + checksFailedPercent: 5 + roundTripTimeMs: 100 + } + testFrequencySec: 30 + } + ] + testGroups: [ + { + destinations: [ + 'Bing' + ] + disable: false + name: 'test-http-Bing' + sources: [ + 'subnet-001(${resourceGroup.name})' + ] + testConfigurations: [ + 'HTTP Bing Test' + ] + } + ] + workspaceResourceId: '' + } + ] + flowLogs: [ + { + enabled: false + storageId: '' + targetResourceId: '' + } + { + formatVersion: 1 + name: 'nnwwaf-fl-001' + retentionInDays: 8 + storageId: '' + targetResourceId: '' + trafficAnalyticsInterval: 10 + workspaceResourceId: '' + } + ] + location: '' + name: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "connectionMonitors": { + "value": [ + { + "endpoints": [ + { + "name": "", + "resourceId": "", + "type": "AzureVM" + }, + { + "address": "www.bing.com", + "name": "Bing", + "type": "ExternalAddress" + } + ], + "name": "nnwwaf-cm-001", + "testConfigurations": [ + { + "httpConfiguration": { + "method": "Get", + "port": 80, + "preferHTTPS": false, + "requestHeaders": [], + "validStatusCodeRanges": [ + "200" + ] + }, + "name": "HTTP Bing Test", + "protocol": "Http", + "successThreshold": { + "checksFailedPercent": 5, + "roundTripTimeMs": 100 + }, + "testFrequencySec": 30 + } + ], + "testGroups": [ + { + "destinations": [ + "Bing" + ], + "disable": false, + "name": "test-http-Bing", + "sources": [ + "subnet-001(${resourceGroup.name})" + ], + "testConfigurations": [ + "HTTP Bing Test" + ] + } + ], + "workspaceResourceId": "" + } + ] + }, + "flowLogs": { + "value": [ + { + "enabled": false, + "storageId": "", + "targetResourceId": "" + }, + { + "formatVersion": 1, + "name": "nnwwaf-fl-001", + "retentionInDays": 8, + "storageId": "", + "targetResourceId": "", + "trafficAnalyticsInterval": 10, + "workspaceResourceId": "" + } + ] + }, + "location": { + "value": "" + }, + "name": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ + +## Parameters + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`connectionMonitors`](#parameter-connectionmonitors) | array | Array that contains the Connection Monitors. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`flowLogs`](#parameter-flowlogs) | array | Array that contains the Flow Logs. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`name`](#parameter-name) | string | Name of the Network Watcher resource (hidden). | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `connectionMonitors` + +Array that contains the Connection Monitors. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `flowLogs` + +Array that contains the Flow Logs. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `name` + +Name of the Network Watcher resource (hidden). + +- Required: No +- Type: string +- Default: `[format('NetworkWatcher_{0}', parameters('location'))]` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed network watcher. | +| `resourceGroupName` | string | The resource group the network watcher was deployed into. | +| `resourceId` | string | The resource ID of the deployed network watcher. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/network-watcher/connection-monitor/README.md b/avm/res/network/network-watcher/connection-monitor/README.md new file mode 100644 index 0000000000..8ca9cfd750 --- /dev/null +++ b/avm/res/network/network-watcher/connection-monitor/README.md @@ -0,0 +1,117 @@ +# Network Watchers Connection Monitors `[Microsoft.Network/networkWatchers/connectionMonitors]` + +This module deploys a Network Watcher Connection Monitor. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkWatchers/connectionMonitors` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkWatchers/connectionMonitors) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the resource. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`endpoints`](#parameter-endpoints) | array | List of connection monitor endpoints. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`networkWatcherName`](#parameter-networkwatchername) | string | Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`testConfigurations`](#parameter-testconfigurations) | array | List of connection monitor test configurations. | +| [`testGroups`](#parameter-testgroups) | array | List of connection monitor test groups. | +| [`workspaceResourceId`](#parameter-workspaceresourceid) | string | Specify the Log Analytics Workspace Resource ID. | + +### Parameter: `name` + +Name of the resource. + +- Required: Yes +- Type: string + +### Parameter: `endpoints` + +List of connection monitor endpoints. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `networkWatcherName` + +Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG. + +- Required: No +- Type: string +- Default: `[format('NetworkWatcher_{0}', resourceGroup().location)]` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `testConfigurations` + +List of connection monitor test configurations. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `testGroups` + +List of connection monitor test groups. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `workspaceResourceId` + +Specify the Log Analytics Workspace Resource ID. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed connection monitor. | +| `resourceGroupName` | string | The resource group the connection monitor was deployed into. | +| `resourceId` | string | The resource ID of the deployed connection monitor. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/network-watcher/connection-monitor/main.bicep b/avm/res/network/network-watcher/connection-monitor/main.bicep new file mode 100644 index 0000000000..0823dabe66 --- /dev/null +++ b/avm/res/network/network-watcher/connection-monitor/main.bicep @@ -0,0 +1,66 @@ +metadata name = 'Network Watchers Connection Monitors' +metadata description = 'This module deploys a Network Watcher Connection Monitor.' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG.') +param networkWatcherName string = 'NetworkWatcher_${resourceGroup().location}' + +@description('Required. Name of the resource.') +param name string + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. List of connection monitor endpoints.') +param endpoints array = [] + +@description('Optional. List of connection monitor test configurations.') +param testConfigurations array = [] + +@description('Optional. List of connection monitor test groups.') +param testGroups array = [] + +@description('Optional. Specify the Log Analytics Workspace Resource ID.') +param workspaceResourceId string = '' + + +resource networkWatcher 'Microsoft.Network/networkWatchers@2023-04-01' existing = { + name: networkWatcherName +} + +resource connectionMonitor 'Microsoft.Network/networkWatchers/connectionMonitors@2023-04-01' = { + name: name + parent: networkWatcher + tags: tags + location: location + properties: { + endpoints: endpoints + testConfigurations: testConfigurations + testGroups: testGroups + outputs: !empty(workspaceResourceId) + ? [ + { + type: 'Workspace' + workspaceSettings: { + workspaceResourceId: workspaceResourceId + } + } + ] + : null + } +} + +@description('The name of the deployed connection monitor.') +output name string = connectionMonitor.name + +@description('The resource ID of the deployed connection monitor.') +output resourceId string = connectionMonitor.id + +@description('The resource group the connection monitor was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = connectionMonitor.location diff --git a/avm/res/network/network-watcher/connection-monitor/main.json b/avm/res/network/network-watcher/connection-monitor/main.json new file mode 100644 index 0000000000..6c3fd5c843 --- /dev/null +++ b/avm/res/network/network-watcher/connection-monitor/main.json @@ -0,0 +1,126 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "13403868700445795620" + }, + "name": "Network Watchers Connection Monitors", + "description": "This module deploys a Network Watcher Connection Monitor.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "networkWatcherName": { + "type": "string", + "defaultValue": "[format('NetworkWatcher_{0}', resourceGroup().location)]", + "metadata": { + "description": "Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "endpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of connection monitor endpoints." + } + }, + "testConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of connection monitor test configurations." + } + }, + "testGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of connection monitor test groups." + } + }, + "workspaceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specify the Log Analytics Workspace Resource ID." + } + } + }, + "resources": { + "networkWatcher": { + "existing": true, + "type": "Microsoft.Network/networkWatchers", + "apiVersion": "2023-04-01", + "name": "[parameters('networkWatcherName')]" + }, + "connectionMonitor": { + "type": "Microsoft.Network/networkWatchers/connectionMonitors", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('networkWatcherName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "properties": { + "endpoints": "[parameters('endpoints')]", + "testConfigurations": "[parameters('testConfigurations')]", + "testGroups": "[parameters('testGroups')]", + "outputs": "[if(not(empty(parameters('workspaceResourceId'))), createArray(createObject('type', 'Workspace', 'workspaceSettings', createObject('workspaceResourceId', parameters('workspaceResourceId')))), null())]" + }, + "dependsOn": [ + "networkWatcher" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed connection monitor." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed connection monitor." + }, + "value": "[resourceId('Microsoft.Network/networkWatchers/connectionMonitors', parameters('networkWatcherName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the connection monitor was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('connectionMonitor', '2023-04-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/network-watcher/flow-log/README.md b/avm/res/network/network-watcher/flow-log/README.md new file mode 100644 index 0000000000..93c1ac0144 --- /dev/null +++ b/avm/res/network/network-watcher/flow-log/README.md @@ -0,0 +1,158 @@ +# NSG Flow Logs `[Microsoft.Network/networkWatchers/flowLogs]` + +This module controls the Network Security Group Flow Logs and analytics settings. +**Note: this module must be run on the Resource Group where Network Watcher is deployed** + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkWatchers/flowLogs` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkWatchers/flowLogs) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`storageId`](#parameter-storageid) | string | Resource ID of the diagnostic storage account. | +| [`targetResourceId`](#parameter-targetresourceid) | string | Resource ID of the NSG that must be enabled for Flow Logs. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-enabled) | bool | If the flow log should be enabled. | +| [`formatVersion`](#parameter-formatversion) | int | The flow log format version. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`name`](#parameter-name) | string | Name of the resource. | +| [`networkWatcherName`](#parameter-networkwatchername) | string | Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG. | +| [`retentionInDays`](#parameter-retentionindays) | int | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`trafficAnalyticsInterval`](#parameter-trafficanalyticsinterval) | int | The interval in minutes which would decide how frequently TA service should do flow analytics. | +| [`workspaceResourceId`](#parameter-workspaceresourceid) | string | Specify the Log Analytics Workspace Resource ID. | + +### Parameter: `storageId` + +Resource ID of the diagnostic storage account. + +- Required: Yes +- Type: string + +### Parameter: `targetResourceId` + +Resource ID of the NSG that must be enabled for Flow Logs. + +- Required: Yes +- Type: string + +### Parameter: `enabled` + +If the flow log should be enabled. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `formatVersion` + +The flow log format version. + +- Required: No +- Type: int +- Default: `2` +- Allowed: + ```Bicep + [ + 1 + 2 + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `name` + +Name of the resource. + +- Required: No +- Type: string +- Default: `[format('{0}-{1}-flowlog', last(split(parameters('targetResourceId'), '/')), split(parameters('targetResourceId'), '/')[4])]` + +### Parameter: `networkWatcherName` + +Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG. + +- Required: No +- Type: string +- Default: `[format('NetworkWatcher_{0}', resourceGroup().location)]` + +### Parameter: `retentionInDays` + +Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. + +- Required: No +- Type: int +- Default: `365` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `trafficAnalyticsInterval` + +The interval in minutes which would decide how frequently TA service should do flow analytics. + +- Required: No +- Type: int +- Default: `60` +- Allowed: + ```Bicep + [ + 10 + 60 + ] + ``` + +### Parameter: `workspaceResourceId` + +Specify the Log Analytics Workspace Resource ID. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the flow log. | +| `resourceGroupName` | string | The resource group the flow log was deployed into. | +| `resourceId` | string | The resource ID of the flow log. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/network-watcher/flow-log/main.bicep b/avm/res/network/network-watcher/flow-log/main.bicep new file mode 100644 index 0000000000..89a14ccc38 --- /dev/null +++ b/avm/res/network/network-watcher/flow-log/main.bicep @@ -0,0 +1,97 @@ +metadata name = 'NSG Flow Logs' +metadata description = '''This module controls the Network Security Group Flow Logs and analytics settings. +**Note: this module must be run on the Resource Group where Network Watcher is deployed**''' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG.') +param networkWatcherName string = 'NetworkWatcher_${resourceGroup().location}' + +@description('Optional. Name of the resource.') +param name string = '${last(split(targetResourceId, '/'))}-${split(targetResourceId, '/')[4]}-flowlog' + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Resource ID of the NSG that must be enabled for Flow Logs.') +param targetResourceId string + +@description('Required. Resource ID of the diagnostic storage account.') +param storageId string + +@description('Optional. If the flow log should be enabled.') +param enabled bool = true + +@description('Optional. The flow log format version.') +@allowed([ + 1 + 2 +]) +param formatVersion int = 2 + +@description('Optional. Specify the Log Analytics Workspace Resource ID.') +param workspaceResourceId string = '' + +@description('Optional. The interval in minutes which would decide how frequently TA service should do flow analytics.') +@allowed([ + 10 + 60 +]) +param trafficAnalyticsInterval int = 60 + +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param retentionInDays int = 365 + +var flowAnalyticsConfiguration = !empty(workspaceResourceId) && enabled == true + ? { + networkWatcherFlowAnalyticsConfiguration: { + enabled: true + workspaceResourceId: workspaceResourceId + trafficAnalyticsInterval: trafficAnalyticsInterval + } + } + : { + networkWatcherFlowAnalyticsConfiguration: { + enabled: false + } + } + +resource networkWatcher 'Microsoft.Network/networkWatchers@2023-04-01' existing = { + name: networkWatcherName +} + +resource flowLog 'Microsoft.Network/networkWatchers/flowLogs@2023-04-01' = { + name: name + parent: networkWatcher + tags: tags + location: location + properties: { + targetResourceId: targetResourceId + storageId: storageId + enabled: enabled + retentionPolicy: { + days: retentionInDays + enabled: retentionInDays == 0 ? false : true + } + format: { + type: 'JSON' + version: formatVersion + } + flowAnalyticsConfiguration: flowAnalyticsConfiguration + } +} +@description('The name of the flow log.') +output name string = flowLog.name + +@description('The resource ID of the flow log.') +output resourceId string = flowLog.id + +@description('The resource group the flow log was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = flowLog.location diff --git a/avm/res/network/network-watcher/flow-log/main.json b/avm/res/network/network-watcher/flow-log/main.json new file mode 100644 index 0000000000..1338480468 --- /dev/null +++ b/avm/res/network/network-watcher/flow-log/main.json @@ -0,0 +1,167 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "7721211688474892554" + }, + "name": "NSG Flow Logs", + "description": "This module controls the Network Security Group Flow Logs and analytics settings.\n**Note: this module must be run on the Resource Group where Network Watcher is deployed**", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "networkWatcherName": { + "type": "string", + "defaultValue": "[format('NetworkWatcher_{0}', resourceGroup().location)]", + "metadata": { + "description": "Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}-flowlog', last(split(parameters('targetResourceId'), '/')), split(parameters('targetResourceId'), '/')[4])]", + "metadata": { + "description": "Optional. Name of the resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "targetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the NSG that must be enabled for Flow Logs." + } + }, + "storageId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the diagnostic storage account." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If the flow log should be enabled." + } + }, + "formatVersion": { + "type": "int", + "defaultValue": 2, + "allowedValues": [ + 1, + 2 + ], + "metadata": { + "description": "Optional. The flow log format version." + } + }, + "workspaceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specify the Log Analytics Workspace Resource ID." + } + }, + "trafficAnalyticsInterval": { + "type": "int", + "defaultValue": 60, + "allowedValues": [ + 10, + 60 + ], + "metadata": { + "description": "Optional. The interval in minutes which would decide how frequently TA service should do flow analytics." + } + }, + "retentionInDays": { + "type": "int", + "defaultValue": 365, + "minValue": 0, + "maxValue": 365, + "metadata": { + "description": "Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely." + } + } + }, + "variables": { + "flowAnalyticsConfiguration": "[if(and(not(empty(parameters('workspaceResourceId'))), equals(parameters('enabled'), true())), createObject('networkWatcherFlowAnalyticsConfiguration', createObject('enabled', true(), 'workspaceResourceId', parameters('workspaceResourceId'), 'trafficAnalyticsInterval', parameters('trafficAnalyticsInterval'))), createObject('networkWatcherFlowAnalyticsConfiguration', createObject('enabled', false())))]" + }, + "resources": { + "networkWatcher": { + "existing": true, + "type": "Microsoft.Network/networkWatchers", + "apiVersion": "2023-04-01", + "name": "[parameters('networkWatcherName')]" + }, + "flowLog": { + "type": "Microsoft.Network/networkWatchers/flowLogs", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('networkWatcherName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "properties": { + "targetResourceId": "[parameters('targetResourceId')]", + "storageId": "[parameters('storageId')]", + "enabled": "[parameters('enabled')]", + "retentionPolicy": { + "days": "[parameters('retentionInDays')]", + "enabled": "[if(equals(parameters('retentionInDays'), 0), false(), true())]" + }, + "format": { + "type": "JSON", + "version": "[parameters('formatVersion')]" + }, + "flowAnalyticsConfiguration": "[variables('flowAnalyticsConfiguration')]" + }, + "dependsOn": [ + "networkWatcher" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the flow log." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the flow log." + }, + "value": "[resourceId('Microsoft.Network/networkWatchers/flowLogs', parameters('networkWatcherName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the flow log was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('flowLog', '2023-04-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/network-watcher/main.bicep b/avm/res/network/network-watcher/main.bicep new file mode 100644 index 0000000000..71f388d98f --- /dev/null +++ b/avm/res/network/network-watcher/main.bicep @@ -0,0 +1,188 @@ +metadata name = 'Network Watchers' +metadata description = 'This module deploys a Network Watcher.' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. Name of the Network Watcher resource (hidden).') +@minLength(1) +param name string = 'NetworkWatcher_${location}' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array that contains the Connection Monitors.') +param connectionMonitors array = [] + +@description('Optional. Array that contains the Flow Logs.') +param flowLogs array = [] + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Network Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '4d97b98b-1d4f-4787-a291-c67834d212e7' + ) + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.network-networkwatcher.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +resource networkWatcher 'Microsoft.Network/networkWatchers@2023-04-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource networkWatcher_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: networkWatcher + } + +resource networkWatcher_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(networkWatcher.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: networkWatcher + } +] + +module networkWatcher_connectionMonitors 'connection-monitor/main.bicep' = [ + for (connectionMonitor, index) in connectionMonitors: { + name: '${uniqueString(deployment().name, location)}-NW-ConnectionMonitor-${index}' + params: { + endpoints: contains(connectionMonitor, 'endpoints') ? connectionMonitor.endpoints : [] + name: connectionMonitor.name + location: location + networkWatcherName: networkWatcher.name + testConfigurations: contains(connectionMonitor, 'testConfigurations') ? connectionMonitor.testConfigurations : [] + testGroups: contains(connectionMonitor, 'testGroups') ? connectionMonitor.testGroups : [] + workspaceResourceId: contains(connectionMonitor, 'workspaceResourceId') + ? connectionMonitor.workspaceResourceId + : '' + } + } +] + +module networkWatcher_flowLogs 'flow-log/main.bicep' = [ + for (flowLog, index) in flowLogs: { + name: '${uniqueString(deployment().name, location)}-NW-FlowLog-${index}' + params: { + enabled: contains(flowLog, 'enabled') ? flowLog.enabled : true + formatVersion: contains(flowLog, 'formatVersion') ? flowLog.formatVersion : 2 + location: contains(flowLog, 'location') ? flowLog.location : location + name: contains(flowLog, 'name') + ? flowLog.name + : '${last(split(flowLog.targetResourceId, '/'))}-${split(flowLog.targetResourceId, '/')[4]}-flowlog' + networkWatcherName: networkWatcher.name + retentionInDays: contains(flowLog, 'retentionInDays') ? flowLog.retentionInDays : 365 + storageId: flowLog.storageId + targetResourceId: flowLog.targetResourceId + trafficAnalyticsInterval: contains(flowLog, 'trafficAnalyticsInterval') ? flowLog.trafficAnalyticsInterval : 60 + workspaceResourceId: contains(flowLog, 'workspaceResourceId') ? flowLog.workspaceResourceId : '' + } + } +] + +@description('The name of the deployed network watcher.') +output name string = networkWatcher.name + +@description('The resource ID of the deployed network watcher.') +output resourceId string = networkWatcher.id + +@description('The resource group the network watcher was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = networkWatcher.location + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @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? + + @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? +}[]? diff --git a/avm/res/network/network-watcher/main.json b/avm/res/network/network-watcher/main.json new file mode 100644 index 0000000000..1a74c3d9f4 --- /dev/null +++ b/avm/res/network/network-watcher/main.json @@ -0,0 +1,633 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "4063023963915633324" + }, + "name": "Network Watchers", + "description": "This module deploys a Network Watcher.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('NetworkWatcher_{0}', parameters('location'))]", + "minLength": 1, + "metadata": { + "description": "Optional. Name of the Network Watcher resource (hidden)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "connectionMonitors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array that contains the Connection Monitors." + } + }, + "flowLogs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array that contains the Flow Logs." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-networkwatcher.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "networkWatcher": { + "type": "Microsoft.Network/networkWatchers", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": {} + }, + "networkWatcher_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/networkWatchers/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkWatcher" + ] + }, + "networkWatcher_roleAssignments": { + "copy": { + "name": "networkWatcher_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkWatchers/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkWatchers', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkWatcher" + ] + }, + "networkWatcher_connectionMonitors": { + "copy": { + "name": "networkWatcher_connectionMonitors", + "count": "[length(parameters('connectionMonitors'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NW-ConnectionMonitor-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "endpoints": "[if(contains(parameters('connectionMonitors')[copyIndex()], 'endpoints'), createObject('value', parameters('connectionMonitors')[copyIndex()].endpoints), createObject('value', createArray()))]", + "name": { + "value": "[parameters('connectionMonitors')[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkWatcherName": { + "value": "[parameters('name')]" + }, + "testConfigurations": "[if(contains(parameters('connectionMonitors')[copyIndex()], 'testConfigurations'), createObject('value', parameters('connectionMonitors')[copyIndex()].testConfigurations), createObject('value', createArray()))]", + "testGroups": "[if(contains(parameters('connectionMonitors')[copyIndex()], 'testGroups'), createObject('value', parameters('connectionMonitors')[copyIndex()].testGroups), createObject('value', createArray()))]", + "workspaceResourceId": "[if(contains(parameters('connectionMonitors')[copyIndex()], 'workspaceResourceId'), createObject('value', parameters('connectionMonitors')[copyIndex()].workspaceResourceId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "13403868700445795620" + }, + "name": "Network Watchers Connection Monitors", + "description": "This module deploys a Network Watcher Connection Monitor.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "networkWatcherName": { + "type": "string", + "defaultValue": "[format('NetworkWatcher_{0}', resourceGroup().location)]", + "metadata": { + "description": "Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "endpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of connection monitor endpoints." + } + }, + "testConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of connection monitor test configurations." + } + }, + "testGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of connection monitor test groups." + } + }, + "workspaceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specify the Log Analytics Workspace Resource ID." + } + } + }, + "resources": { + "networkWatcher": { + "existing": true, + "type": "Microsoft.Network/networkWatchers", + "apiVersion": "2023-04-01", + "name": "[parameters('networkWatcherName')]" + }, + "connectionMonitor": { + "type": "Microsoft.Network/networkWatchers/connectionMonitors", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('networkWatcherName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "properties": { + "endpoints": "[parameters('endpoints')]", + "testConfigurations": "[parameters('testConfigurations')]", + "testGroups": "[parameters('testGroups')]", + "outputs": "[if(not(empty(parameters('workspaceResourceId'))), createArray(createObject('type', 'Workspace', 'workspaceSettings', createObject('workspaceResourceId', parameters('workspaceResourceId')))), null())]" + }, + "dependsOn": [ + "networkWatcher" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed connection monitor." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed connection monitor." + }, + "value": "[resourceId('Microsoft.Network/networkWatchers/connectionMonitors', parameters('networkWatcherName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the connection monitor was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('connectionMonitor', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "networkWatcher" + ] + }, + "networkWatcher_flowLogs": { + "copy": { + "name": "networkWatcher_flowLogs", + "count": "[length(parameters('flowLogs'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NW-FlowLog-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enabled": "[if(contains(parameters('flowLogs')[copyIndex()], 'enabled'), createObject('value', parameters('flowLogs')[copyIndex()].enabled), createObject('value', true()))]", + "formatVersion": "[if(contains(parameters('flowLogs')[copyIndex()], 'formatVersion'), createObject('value', parameters('flowLogs')[copyIndex()].formatVersion), createObject('value', 2))]", + "location": "[if(contains(parameters('flowLogs')[copyIndex()], 'location'), createObject('value', parameters('flowLogs')[copyIndex()].location), createObject('value', parameters('location')))]", + "name": "[if(contains(parameters('flowLogs')[copyIndex()], 'name'), createObject('value', parameters('flowLogs')[copyIndex()].name), createObject('value', format('{0}-{1}-flowlog', last(split(parameters('flowLogs')[copyIndex()].targetResourceId, '/')), split(parameters('flowLogs')[copyIndex()].targetResourceId, '/')[4])))]", + "networkWatcherName": { + "value": "[parameters('name')]" + }, + "retentionInDays": "[if(contains(parameters('flowLogs')[copyIndex()], 'retentionInDays'), createObject('value', parameters('flowLogs')[copyIndex()].retentionInDays), createObject('value', 365))]", + "storageId": { + "value": "[parameters('flowLogs')[copyIndex()].storageId]" + }, + "targetResourceId": { + "value": "[parameters('flowLogs')[copyIndex()].targetResourceId]" + }, + "trafficAnalyticsInterval": "[if(contains(parameters('flowLogs')[copyIndex()], 'trafficAnalyticsInterval'), createObject('value', parameters('flowLogs')[copyIndex()].trafficAnalyticsInterval), createObject('value', 60))]", + "workspaceResourceId": "[if(contains(parameters('flowLogs')[copyIndex()], 'workspaceResourceId'), createObject('value', parameters('flowLogs')[copyIndex()].workspaceResourceId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "7721211688474892554" + }, + "name": "NSG Flow Logs", + "description": "This module controls the Network Security Group Flow Logs and analytics settings.\n**Note: this module must be run on the Resource Group where Network Watcher is deployed**", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "networkWatcherName": { + "type": "string", + "defaultValue": "[format('NetworkWatcher_{0}', resourceGroup().location)]", + "metadata": { + "description": "Optional. Name of the network watcher resource. Must be in the resource group where the Flow log will be created and same region as the NSG." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}-flowlog', last(split(parameters('targetResourceId'), '/')), split(parameters('targetResourceId'), '/')[4])]", + "metadata": { + "description": "Optional. Name of the resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "targetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the NSG that must be enabled for Flow Logs." + } + }, + "storageId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the diagnostic storage account." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If the flow log should be enabled." + } + }, + "formatVersion": { + "type": "int", + "defaultValue": 2, + "allowedValues": [ + 1, + 2 + ], + "metadata": { + "description": "Optional. The flow log format version." + } + }, + "workspaceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specify the Log Analytics Workspace Resource ID." + } + }, + "trafficAnalyticsInterval": { + "type": "int", + "defaultValue": 60, + "allowedValues": [ + 10, + 60 + ], + "metadata": { + "description": "Optional. The interval in minutes which would decide how frequently TA service should do flow analytics." + } + }, + "retentionInDays": { + "type": "int", + "defaultValue": 365, + "minValue": 0, + "maxValue": 365, + "metadata": { + "description": "Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely." + } + } + }, + "variables": { + "flowAnalyticsConfiguration": "[if(and(not(empty(parameters('workspaceResourceId'))), equals(parameters('enabled'), true())), createObject('networkWatcherFlowAnalyticsConfiguration', createObject('enabled', true(), 'workspaceResourceId', parameters('workspaceResourceId'), 'trafficAnalyticsInterval', parameters('trafficAnalyticsInterval'))), createObject('networkWatcherFlowAnalyticsConfiguration', createObject('enabled', false())))]" + }, + "resources": { + "networkWatcher": { + "existing": true, + "type": "Microsoft.Network/networkWatchers", + "apiVersion": "2023-04-01", + "name": "[parameters('networkWatcherName')]" + }, + "flowLog": { + "type": "Microsoft.Network/networkWatchers/flowLogs", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('networkWatcherName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "properties": { + "targetResourceId": "[parameters('targetResourceId')]", + "storageId": "[parameters('storageId')]", + "enabled": "[parameters('enabled')]", + "retentionPolicy": { + "days": "[parameters('retentionInDays')]", + "enabled": "[if(equals(parameters('retentionInDays'), 0), false(), true())]" + }, + "format": { + "type": "JSON", + "version": "[parameters('formatVersion')]" + }, + "flowAnalyticsConfiguration": "[variables('flowAnalyticsConfiguration')]" + }, + "dependsOn": [ + "networkWatcher" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the flow log." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the flow log." + }, + "value": "[resourceId('Microsoft.Network/networkWatchers/flowLogs', parameters('networkWatcherName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the flow log was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('flowLog', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "networkWatcher" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed network watcher." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed network watcher." + }, + "value": "[resourceId('Microsoft.Network/networkWatchers', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network watcher was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkWatcher', '2023-04-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/network-watcher/tests/e2e/defaults/main.test.bicep b/avm/res/network/network-watcher/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..9fcca025ef --- /dev/null +++ b/avm/res/network/network-watcher/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'NetworkWatcherRG' // Note, this is the default NetworkWatcher resource group. Do not change. + +@description('Optional. The location to deploy resources to.') +#disable-next-line no-unused-params // A rotation location cannot be used for this test as NetworkWatcher Resource Groups are created for any test location automatically +param resourceLocation string = deployment().location + +@description('Optional. The static location of the resource group & resources.') // Note, we set the location of the NetworkWatcherRG to avoid conflicts with the already existing NetworkWatcherRG +param tempLocation string = 'uksouth' + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nnwmin' + +#disable-next-line no-unused-params +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: tempLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, tempLocation)}-test-${serviceShort}-${iteration}' + params: { + // Note: This value is not required and only set to enable testing + location: tempLocation + } +}] diff --git a/avm/res/network/network-watcher/tests/e2e/max/dependencies.bicep b/avm/res/network/network-watcher/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..c20f841f30 --- /dev/null +++ b/avm/res/network/network-watcher/tests/e2e/max/dependencies.bicep @@ -0,0 +1,144 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the first Network Security Group to create.') +param firstNetworkSecurityGroupName string + +@description('Required. The name of the second Network Security Group to create.') +param secondNetworkSecurityGroupName string + +@description('Required. The name of the Virtual Machine to create.') +param virtualMachineName string + +@description('Optional. The password to leverage for the VM login.') +@secure() +param password string = newGuid() + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource firstNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: firstNetworkSecurityGroupName + location: location +} + +resource secondNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: secondNetworkSecurityGroupName + location: location +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2023-04-01' = { + name: '${virtualMachineName}-nic' + location: location + properties: { + ipConfigurations: [ + { + name: 'ipconfig01' + properties: { + subnet: { + id: virtualNetwork.properties.subnets[0].id + } + } + } + ] + } +} + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2022-08-01' = { + name: virtualMachineName + location: location + properties: { + networkProfile: { + networkInterfaces: [ + { + id: networkInterface.id + properties: { + deleteOption: 'Delete' + primary: true + } + } + ] + } + storageProfile: { + imageReference: { + publisher: 'Canonical' + offer: '0001-com-ubuntu-server-jammy' + sku: '22_04-lts-gen2' + version: 'latest' + } + osDisk: { + deleteOption: 'Delete' + createOption: 'FromImage' + } + } + hardwareProfile: { + vmSize: 'Standard_B1ms' + } + osProfile: { + adminUsername: '${virtualMachineName}cake' + adminPassword: password + computerName: virtualMachineName + linuxConfiguration: { + disablePasswordAuthentication: false + } + } + } +} + +resource extension 'Microsoft.Compute/virtualMachines/extensions@2021-07-01' = { + name: 'NetworkWatcherAgent' + parent: virtualMachine + location: location + properties: { + publisher: 'Microsoft.Azure.NetworkWatcher' + type: 'NetworkWatcherAgentLinux' + typeHandlerVersion: '1.4' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: false + settings: {} + protectedSettings: {} + suppressFailures: false + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Virtual Machine.') +output virtualMachineResourceId string = virtualMachine.id + +@description('The resource ID of the first created Network Security Group.') +output firstNetworkSecurityGroupResourceId string = firstNetworkSecurityGroup.id + +@description('The resource ID of the second created Network Security Group.') +output secondNetworkSecurityGroupResourceId string = secondNetworkSecurityGroup.id diff --git a/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep b/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..4386fe3d6e --- /dev/null +++ b/avm/res/network/network-watcher/tests/e2e/max/main.test.bicep @@ -0,0 +1,172 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'NetworkWatcherRG' // Note, this is the default NetworkWatcher resource group. Do not change. + +@description('Optional. The location to deploy resources to.') +#disable-next-line no-unused-params // A rotation location cannot be used for this test as NetworkWatcher Resource Groups are created for any test location automatically +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nnwmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The static location of the resource group & resources.') // Note, we set the location of the NetworkWatcherRG to avoid conflicts with the already existing NetworkWatcherRG +param tempLocation string = 'uksouth' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: tempLocation +} + +resource resourceGroupDependencies 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: 'dep-${namePrefix}-network.networkwatcher-${serviceShort}-rg' + location: tempLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroupDependencies + name: '${uniqueString(deployment().name, tempLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + firstNetworkSecurityGroupName: 'dep-${namePrefix}-nsg-1-${serviceShort}' + secondNetworkSecurityGroupName: 'dep-${namePrefix}-nsg-2-${serviceShort}' + virtualMachineName: 'dep-${namePrefix}-vm-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + location: tempLocation + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroupDependencies + name: '${uniqueString(deployment().name, tempLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: tempLocation + } +} + +// ============== // +// Test Execution // +// ============== // +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, tempLocation)}-test-${serviceShort}-${iteration}' + params: { + name: 'NetworkWatcher_${tempLocation}' + location: tempLocation + connectionMonitors: [ + { + name: '${namePrefix}-${serviceShort}-cm-001' + endpoints: [ + { + name: '${namePrefix}-subnet-001(${resourceGroup.name})' + resourceId: nestedDependencies.outputs.virtualMachineResourceId + type: 'AzureVM' + } + { + address: 'www.bing.com' + name: 'Bing' + type: 'ExternalAddress' + } + ] + testConfigurations: [ + { + httpConfiguration: { + method: 'Get' + port: 80 + preferHTTPS: false + requestHeaders: [] + validStatusCodeRanges: [ + '200' + ] + } + name: 'HTTP Bing Test' + protocol: 'Http' + successThreshold: { + checksFailedPercent: 5 + roundTripTimeMs: 100 + } + testFrequencySec: 30 + } + ] + testGroups: [ + { + destinations: [ + 'Bing' + ] + disable: false + name: 'test-http-Bing' + sources: [ + '${namePrefix}-subnet-001(${resourceGroup.name})' + ] + testConfigurations: [ + 'HTTP Bing Test' + ] + } + ] + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + flowLogs: [ + { + enabled: false + storageId: diagnosticDependencies.outputs.storageAccountResourceId + targetResourceId: nestedDependencies.outputs.firstNetworkSecurityGroupResourceId + } + { + formatVersion: 1 + name: '${namePrefix}-${serviceShort}-fl-001' + retentionInDays: 8 + storageId: diagnosticDependencies.outputs.storageAccountResourceId + targetResourceId: nestedDependencies.outputs.secondNetworkSecurityGroupResourceId + trafficAnalyticsInterval: 10 + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +}] diff --git a/avm/res/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..c20f841f30 --- /dev/null +++ b/avm/res/network/network-watcher/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,144 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the first Network Security Group to create.') +param firstNetworkSecurityGroupName string + +@description('Required. The name of the second Network Security Group to create.') +param secondNetworkSecurityGroupName string + +@description('Required. The name of the Virtual Machine to create.') +param virtualMachineName string + +@description('Optional. The password to leverage for the VM login.') +@secure() +param password string = newGuid() + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource firstNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: firstNetworkSecurityGroupName + location: location +} + +resource secondNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: secondNetworkSecurityGroupName + location: location +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2023-04-01' = { + name: '${virtualMachineName}-nic' + location: location + properties: { + ipConfigurations: [ + { + name: 'ipconfig01' + properties: { + subnet: { + id: virtualNetwork.properties.subnets[0].id + } + } + } + ] + } +} + +resource virtualMachine 'Microsoft.Compute/virtualMachines@2022-08-01' = { + name: virtualMachineName + location: location + properties: { + networkProfile: { + networkInterfaces: [ + { + id: networkInterface.id + properties: { + deleteOption: 'Delete' + primary: true + } + } + ] + } + storageProfile: { + imageReference: { + publisher: 'Canonical' + offer: '0001-com-ubuntu-server-jammy' + sku: '22_04-lts-gen2' + version: 'latest' + } + osDisk: { + deleteOption: 'Delete' + createOption: 'FromImage' + } + } + hardwareProfile: { + vmSize: 'Standard_B1ms' + } + osProfile: { + adminUsername: '${virtualMachineName}cake' + adminPassword: password + computerName: virtualMachineName + linuxConfiguration: { + disablePasswordAuthentication: false + } + } + } +} + +resource extension 'Microsoft.Compute/virtualMachines/extensions@2021-07-01' = { + name: 'NetworkWatcherAgent' + parent: virtualMachine + location: location + properties: { + publisher: 'Microsoft.Azure.NetworkWatcher' + type: 'NetworkWatcherAgentLinux' + typeHandlerVersion: '1.4' + autoUpgradeMinorVersion: true + enableAutomaticUpgrade: false + settings: {} + protectedSettings: {} + suppressFailures: false + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Virtual Machine.') +output virtualMachineResourceId string = virtualMachine.id + +@description('The resource ID of the first created Network Security Group.') +output firstNetworkSecurityGroupResourceId string = firstNetworkSecurityGroup.id + +@description('The resource ID of the second created Network Security Group.') +output secondNetworkSecurityGroupResourceId string = secondNetworkSecurityGroup.id diff --git a/avm/res/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..32fc738840 --- /dev/null +++ b/avm/res/network/network-watcher/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,156 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'NetworkWatcherRG' // Note, this is the default NetworkWatcher resource group. Do not change. + +@description('Optional. The location to deploy resources to.') +#disable-next-line no-unused-params // A rotation location cannot be used for this test as NetworkWatcher Resource Groups are created for any test location automatically +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nnwwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The static location of the resource group & resources.') // Note, we set the location of the NetworkWatcherRG to avoid conflicts with the already existing NetworkWatcherRG +param tempLocation string = 'uksouth' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: tempLocation +} + +resource resourceGroupDependencies 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: 'dep-${namePrefix}-network.networkwatcher-${serviceShort}-rg' + location: tempLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroupDependencies + name: '${uniqueString(deployment().name, tempLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + firstNetworkSecurityGroupName: 'dep-${namePrefix}-nsg-1-${serviceShort}' + secondNetworkSecurityGroupName: 'dep-${namePrefix}-nsg-2-${serviceShort}' + virtualMachineName: 'dep-${namePrefix}-vm-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + location: tempLocation + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroupDependencies + name: '${uniqueString(deployment().name, tempLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: tempLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, tempLocation)}-test-${serviceShort}-${iteration}' + params: { + name: 'NetworkWatcher_${tempLocation}' + location: tempLocation + connectionMonitors: [ + { + name: '${namePrefix}-${serviceShort}-cm-001' + endpoints: [ + { + name: '${namePrefix}-subnet-001(${resourceGroup.name})' + resourceId: nestedDependencies.outputs.virtualMachineResourceId + type: 'AzureVM' + } + { + address: 'www.bing.com' + name: 'Bing' + type: 'ExternalAddress' + } + ] + testConfigurations: [ + { + httpConfiguration: { + method: 'Get' + port: 80 + preferHTTPS: false + requestHeaders: [] + validStatusCodeRanges: [ + '200' + ] + } + name: 'HTTP Bing Test' + protocol: 'Http' + successThreshold: { + checksFailedPercent: 5 + roundTripTimeMs: 100 + } + testFrequencySec: 30 + } + ] + testGroups: [ + { + destinations: [ + 'Bing' + ] + disable: false + name: 'test-http-Bing' + sources: [ + '${namePrefix}-subnet-001(${resourceGroup.name})' + ] + testConfigurations: [ + 'HTTP Bing Test' + ] + } + ] + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + flowLogs: [ + { + enabled: false + storageId: diagnosticDependencies.outputs.storageAccountResourceId + targetResourceId: nestedDependencies.outputs.firstNetworkSecurityGroupResourceId + } + { + formatVersion: 1 + name: '${namePrefix}-${serviceShort}-fl-001' + retentionInDays: 8 + storageId: diagnosticDependencies.outputs.storageAccountResourceId + targetResourceId: nestedDependencies.outputs.secondNetworkSecurityGroupResourceId + trafficAnalyticsInterval: 10 + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +}] diff --git a/avm/res/network/network-watcher/version.json b/avm/res/network/network-watcher/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/network/network-watcher/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 8021bb8f6df6d7b2863c71105a33d7c29ecf752d Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Thu, 11 Apr 2024 11:43:04 +0200 Subject: [PATCH 22/66] fix: Added missing `privateLinkServiceConnectionName` parameter to Private Endpoint UDT (#1614) ## Description To enable users to set an explicit `privateLinkServiceConnectionName` it must be defined in the UDT where it was missing. Updated for all modules that @krbar updated in his preceeding PR https://github.com/Azure/bicep-registry-modules/pull/1472 ![image](https://github.com/Azure/bicep-registry-modules/assets/5365358/ae4213c7-6f67-439d-9b5f-6e1bf1d5a612) ## Pipeline Reference | Pipeline | | -------- | [![avm.res.automation.automation-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml) [![avm.res.batch.batch-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml) [![avm.res.cache.redis](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) [![avm.res.cognitive-services.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml) [![avm.res.data-factory.factory](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml) [![avm.res.digital-twins.digital-twins-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml) [![avm.res.document-db.database-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml) [![avm.res.event-grid.domain](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml) [![avm.res.event-grid.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml) [![avm.res.event-grid.topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml) [![avm.res.insights.private-link-scope](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml) [![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) [![avm.res.purview.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml) [![avm.res.recovery-services.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml) [![avm.res.relay.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml) [![avm.res.search.search-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml) [![avm.res.service-bus.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml) [![avm.res.signal-r-service.signal-r](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml) [![avm.res.signal-r-service.web-pub-sub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml) [![avm.res.sql.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml) [![avm.res.synapse.private-link-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml) [![avm.res.synapse.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml/badge.svg?branch=users%2Falsehr%2FudtLinkName&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../automation/automation-account/README.md | 8 ++++ .../automation/automation-account/main.bicep | 3 ++ .../automation/automation-account/main.json | 9 ++++- avm/res/batch/batch-account/README.md | 8 ++++ avm/res/batch/batch-account/main.bicep | 3 ++ avm/res/batch/batch-account/main.json | 9 ++++- avm/res/cache/redis/README.md | 8 ++++ avm/res/cache/redis/main.bicep | 3 ++ avm/res/cache/redis/main.json | 9 ++++- avm/res/cognitive-services/account/README.md | 8 ++++ avm/res/cognitive-services/account/main.bicep | 3 ++ avm/res/cognitive-services/account/main.json | 9 ++++- avm/res/data-factory/factory/README.md | 8 ++++ avm/res/data-factory/factory/main.bicep | 3 ++ avm/res/data-factory/factory/main.json | 9 ++++- .../digital-twins-instance/README.md | 8 ++++ .../digital-twins-instance/main.bicep | 3 ++ .../digital-twins-instance/main.json | 9 ++++- .../document-db/database-account/README.md | 8 ++++ .../document-db/database-account/main.bicep | 3 ++ .../document-db/database-account/main.json | 9 ++++- avm/res/event-grid/domain/README.md | 8 ++++ avm/res/event-grid/domain/main.bicep | 3 ++ avm/res/event-grid/domain/main.json | 9 ++++- avm/res/event-grid/namespace/README.md | 8 ++++ avm/res/event-grid/namespace/main.bicep | 3 ++ avm/res/event-grid/namespace/main.json | 9 ++++- avm/res/event-grid/topic/README.md | 8 ++++ avm/res/event-grid/topic/main.bicep | 3 ++ avm/res/event-grid/topic/main.json | 9 ++++- avm/res/insights/private-link-scope/README.md | 8 ++++ .../insights/private-link-scope/main.bicep | 3 ++ avm/res/insights/private-link-scope/main.json | 9 ++++- avm/res/key-vault/vault/README.md | 8 ++++ avm/res/key-vault/vault/main.bicep | 3 ++ avm/res/key-vault/vault/main.json | 9 ++++- avm/res/purview/account/README.md | 40 +++++++++++++++++++ avm/res/purview/account/main.bicep | 3 ++ avm/res/purview/account/main.json | 9 ++++- avm/res/recovery-services/vault/README.md | 8 ++++ avm/res/recovery-services/vault/main.bicep | 3 ++ avm/res/recovery-services/vault/main.json | 9 ++++- avm/res/relay/namespace/README.md | 8 ++++ avm/res/relay/namespace/main.bicep | 3 ++ avm/res/relay/namespace/main.json | 9 ++++- avm/res/search/search-service/README.md | 8 ++++ avm/res/search/search-service/main.bicep | 3 ++ avm/res/search/search-service/main.json | 9 ++++- avm/res/service-bus/namespace/README.md | 12 ++++++ avm/res/service-bus/namespace/main.bicep | 3 ++ avm/res/service-bus/namespace/main.json | 9 ++++- .../namespace/tests/e2e/max/main.test.bicep | 2 + avm/res/signal-r-service/signal-r/README.md | 8 ++++ avm/res/signal-r-service/signal-r/main.bicep | 3 ++ avm/res/signal-r-service/signal-r/main.json | 9 ++++- .../signal-r-service/web-pub-sub/README.md | 8 ++++ .../signal-r-service/web-pub-sub/main.bicep | 3 ++ .../signal-r-service/web-pub-sub/main.json | 9 ++++- avm/res/sql/server/README.md | 8 ++++ avm/res/sql/server/main.bicep | 3 ++ avm/res/sql/server/main.json | 9 ++++- avm/res/synapse/private-link-hub/README.md | 8 ++++ avm/res/synapse/private-link-hub/main.bicep | 3 ++ avm/res/synapse/private-link-hub/main.json | 9 ++++- avm/res/synapse/workspace/README.md | 8 ++++ avm/res/synapse/workspace/main.bicep | 3 ++ avm/res/synapse/workspace/main.json | 9 ++++- 67 files changed, 456 insertions(+), 22 deletions(-) diff --git a/avm/res/automation/automation-account/README.md b/avm/res/automation/automation-account/README.md index 27d88eee16..26fd342e22 100644 --- a/avm/res/automation/automation-account/README.md +++ b/avm/res/automation/automation-account/README.md @@ -1463,6 +1463,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | @@ -1665,6 +1666,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/automation/automation-account/main.bicep b/avm/res/automation/automation-account/main.bicep index 4d401435a5..3f579884c4 100644 --- a/avm/res/automation/automation-account/main.bicep +++ b/avm/res/automation/automation-account/main.bicep @@ -584,6 +584,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string diff --git a/avm/res/automation/automation-account/main.json b/avm/res/automation/automation-account/main.json index 758ecb9deb..ef54d7a09b 100644 --- a/avm/res/automation/automation-account/main.json +++ b/avm/res/automation/automation-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "6251765763506796385" + "templateHash": "16001446000186457588" }, "name": "Automation Accounts", "description": "This module deploys an Azure Automation Account.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "metadata": { diff --git a/avm/res/batch/batch-account/README.md b/avm/res/batch/batch-account/README.md index 045ce70467..f2d6ddd938 100644 --- a/avm/res/batch/batch-account/README.md +++ b/avm/res/batch/batch-account/README.md @@ -1093,6 +1093,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | @@ -1295,6 +1296,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/batch/batch-account/main.bicep b/avm/res/batch/batch-account/main.bicep index 84657dc22e..b74ebf3012 100644 --- a/avm/res/batch/batch-account/main.bicep +++ b/avm/res/batch/batch-account/main.bicep @@ -420,6 +420,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string diff --git a/avm/res/batch/batch-account/main.json b/avm/res/batch/batch-account/main.json index 1dcab47561..918b144932 100644 --- a/avm/res/batch/batch-account/main.json +++ b/avm/res/batch/batch-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "6890367843750826610" + "templateHash": "11103817479788393007" }, "name": "Batch Accounts", "description": "This module deploys a Batch Account.", @@ -218,6 +218,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "metadata": { diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index 78ea24c202..a8e005dfd5 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -858,6 +858,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1054,6 +1055,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index 92250dddf6..a7975ce724 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -354,6 +354,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index d1d6787625..0dcf9d3c88 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "14239657026051186935" + "templateHash": "7874819819895628744" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache.", @@ -80,6 +80,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/cognitive-services/account/README.md b/avm/res/cognitive-services/account/README.md index ad3aa7e878..587b599762 100644 --- a/avm/res/cognitive-services/account/README.md +++ b/avm/res/cognitive-services/account/README.md @@ -1241,6 +1241,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1437,6 +1438,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/cognitive-services/account/main.bicep b/avm/res/cognitive-services/account/main.bicep index fdac84cba9..d88001d7f3 100644 --- a/avm/res/cognitive-services/account/main.bicep +++ b/avm/res/cognitive-services/account/main.bicep @@ -549,6 +549,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/cognitive-services/account/main.json b/avm/res/cognitive-services/account/main.json index 35f3b05f87..db28c215b9 100644 --- a/avm/res/cognitive-services/account/main.json +++ b/avm/res/cognitive-services/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "4464138557763734337" + "templateHash": "1728463055074429069" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service.", @@ -218,6 +218,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/data-factory/factory/README.md b/avm/res/data-factory/factory/README.md index bda24aa203..7dab0aa926 100644 --- a/avm/res/data-factory/factory/README.md +++ b/avm/res/data-factory/factory/README.md @@ -912,6 +912,7 @@ Configuration Details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1108,6 +1109,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/data-factory/factory/main.bicep b/avm/res/data-factory/factory/main.bicep index c82317923d..b8214de937 100644 --- a/avm/res/data-factory/factory/main.bicep +++ b/avm/res/data-factory/factory/main.bicep @@ -408,6 +408,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/data-factory/factory/main.json b/avm/res/data-factory/factory/main.json index aad72cb4c9..660811ad99 100644 --- a/avm/res/data-factory/factory/main.json +++ b/avm/res/data-factory/factory/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "14187397977187427796" + "templateHash": "15112622274427305357" }, "name": "Data Factories", "description": "This module deploys a Data Factory.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/digital-twins/digital-twins-instance/README.md b/avm/res/digital-twins/digital-twins-instance/README.md index 1c7a729b3b..61d336db5c 100644 --- a/avm/res/digital-twins/digital-twins-instance/README.md +++ b/avm/res/digital-twins/digital-twins-instance/README.md @@ -780,6 +780,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Privte Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -976,6 +977,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Privte Endpoint into a different resource group than the main resource. diff --git a/avm/res/digital-twins/digital-twins-instance/main.bicep b/avm/res/digital-twins/digital-twins-instance/main.bicep index 7343b751e7..e1c317709d 100644 --- a/avm/res/digital-twins/digital-twins-instance/main.bicep +++ b/avm/res/digital-twins/digital-twins-instance/main.bicep @@ -362,6 +362,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/digital-twins/digital-twins-instance/main.json b/avm/res/digital-twins/digital-twins-instance/main.json index 50fc49a6d8..be1138a612 100644 --- a/avm/res/digital-twins/digital-twins-instance/main.json +++ b/avm/res/digital-twins/digital-twins-instance/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "592119492280088110" + "templateHash": "16174863317053860668" }, "name": "Digital Twins Instances", "description": "This module deploys an Azure Digital Twins Instance.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/document-db/database-account/README.md b/avm/res/document-db/database-account/README.md index 6ae99ad652..fa45217f9c 100644 --- a/avm/res/document-db/database-account/README.md +++ b/avm/res/document-db/database-account/README.md @@ -2772,6 +2772,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | @@ -2974,6 +2975,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/document-db/database-account/main.bicep b/avm/res/document-db/database-account/main.bicep index 353f3a673f..5cbfbfe85d 100644 --- a/avm/res/document-db/database-account/main.bicep +++ b/avm/res/document-db/database-account/main.bicep @@ -599,6 +599,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string diff --git a/avm/res/document-db/database-account/main.json b/avm/res/document-db/database-account/main.json index bb1a275336..943fa8af42 100644 --- a/avm/res/document-db/database-account/main.json +++ b/avm/res/document-db/database-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "4437379960992955499" + "templateHash": "9794166259717067711" }, "name": "DocumentDB Database Accounts", "description": "This module deploys a DocumentDB Database Account.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "metadata": { diff --git a/avm/res/event-grid/domain/README.md b/avm/res/event-grid/domain/README.md index ae021496d2..11e5d6818e 100644 --- a/avm/res/event-grid/domain/README.md +++ b/avm/res/event-grid/domain/README.md @@ -745,6 +745,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -941,6 +942,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/event-grid/domain/main.bicep b/avm/res/event-grid/domain/main.bicep index 9470ab66dd..0473c974c1 100644 --- a/avm/res/event-grid/domain/main.bicep +++ b/avm/res/event-grid/domain/main.bicep @@ -324,6 +324,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/event-grid/domain/main.json b/avm/res/event-grid/domain/main.json index 00d1d38a99..f47aa20bf6 100644 --- a/avm/res/event-grid/domain/main.json +++ b/avm/res/event-grid/domain/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "16218993533368650481" + "templateHash": "17171097905179773848" }, "name": "Event Grid Domains", "description": "This module deploys an Event Grid Domain.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/event-grid/namespace/README.md b/avm/res/event-grid/namespace/README.md index 856afe7f04..3be6075be5 100644 --- a/avm/res/event-grid/namespace/README.md +++ b/avm/res/event-grid/namespace/README.md @@ -1710,6 +1710,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1906,6 +1907,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/event-grid/namespace/main.bicep b/avm/res/event-grid/namespace/main.bicep index 56edb55bdb..e76d65e7c2 100644 --- a/avm/res/event-grid/namespace/main.bicep +++ b/avm/res/event-grid/namespace/main.bicep @@ -491,6 +491,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/event-grid/namespace/main.json b/avm/res/event-grid/namespace/main.json index f5d24919c4..45f6ea4550 100644 --- a/avm/res/event-grid/namespace/main.json +++ b/avm/res/event-grid/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "16128168138494478881" + "templateHash": "18358017202196196766" }, "name": "Event Grid Namespaces", "description": "This module deploys an Event Grid Namespace.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/event-grid/topic/README.md b/avm/res/event-grid/topic/README.md index c611785a80..c589fdc2a3 100644 --- a/avm/res/event-grid/topic/README.md +++ b/avm/res/event-grid/topic/README.md @@ -839,6 +839,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1035,6 +1036,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/event-grid/topic/main.bicep b/avm/res/event-grid/topic/main.bicep index 6f23833d93..b99f1f0bae 100644 --- a/avm/res/event-grid/topic/main.bicep +++ b/avm/res/event-grid/topic/main.bicep @@ -334,6 +334,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/event-grid/topic/main.json b/avm/res/event-grid/topic/main.json index 5d7ac45336..8cf80a5253 100644 --- a/avm/res/event-grid/topic/main.json +++ b/avm/res/event-grid/topic/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "3980392845295222742" + "templateHash": "16219476239317554941" }, "name": "Event Grid Topics", "description": "This module deploys an Event Grid Topic.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/insights/private-link-scope/README.md b/avm/res/insights/private-link-scope/README.md index ae2484afd1..33afa6a34b 100644 --- a/avm/res/insights/private-link-scope/README.md +++ b/avm/res/insights/private-link-scope/README.md @@ -941,6 +941,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1137,6 +1138,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/insights/private-link-scope/main.bicep b/avm/res/insights/private-link-scope/main.bicep index 111bb87167..787d7aaafa 100644 --- a/avm/res/insights/private-link-scope/main.bicep +++ b/avm/res/insights/private-link-scope/main.bicep @@ -271,6 +271,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/insights/private-link-scope/main.json b/avm/res/insights/private-link-scope/main.json index b0c5d75a20..8066330d58 100644 --- a/avm/res/insights/private-link-scope/main.json +++ b/avm/res/insights/private-link-scope/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "3043663816660820718" + "templateHash": "15594611995771551731" }, "name": "Azure Monitor Private Link Scopes", "description": "This module deploys an Azure Monitor Private Link Scope.", @@ -123,6 +123,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/key-vault/vault/README.md b/avm/res/key-vault/vault/README.md index 54df61a844..f86a29fbc3 100644 --- a/avm/res/key-vault/vault/README.md +++ b/avm/res/key-vault/vault/README.md @@ -1320,6 +1320,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1516,6 +1517,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/key-vault/vault/main.bicep b/avm/res/key-vault/vault/main.bicep index 4fa14810d9..57ee7e3197 100644 --- a/avm/res/key-vault/vault/main.bicep +++ b/avm/res/key-vault/vault/main.bicep @@ -459,6 +459,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/key-vault/vault/main.json b/avm/res/key-vault/vault/main.json index 903efb61de..9a39a19b56 100644 --- a/avm/res/key-vault/vault/main.json +++ b/avm/res/key-vault/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "7617547098704473732" + "templateHash": "15417452365743513256" }, "name": "Key Vaults", "description": "This module deploys a Key Vault.", @@ -218,6 +218,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/purview/account/README.md b/avm/res/purview/account/README.md index e8c88a8d1b..5f52f7ce62 100644 --- a/avm/res/purview/account/README.md +++ b/avm/res/purview/account/README.md @@ -667,6 +667,7 @@ Configuration details for Purview Account private endpoints. For security reason | [`name`](#parameter-accountprivateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-accountprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-accountprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-accountprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-accountprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-accountprivateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-accountprivateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -863,6 +864,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `accountPrivateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `accountPrivateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. @@ -1156,6 +1164,7 @@ Configuration details for Purview Managed Event Hub namespace private endpoints. | [`name`](#parameter-eventhubprivateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-eventhubprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-eventhubprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-eventhubprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-eventhubprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-eventhubprivateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-eventhubprivateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1352,6 +1361,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `eventHubPrivateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `eventHubPrivateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. @@ -1563,6 +1579,7 @@ Configuration details for Purview Portal private endpoints. For security reasons | [`name`](#parameter-portalprivateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-portalprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-portalprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-portalprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-portalprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-portalprivateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-portalprivateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1759,6 +1776,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `portalPrivateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `portalPrivateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. @@ -2003,6 +2027,7 @@ Configuration details for Purview Managed Storage Account blob private endpoints | [`name`](#parameter-storageblobprivateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-storageblobprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-storageblobprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-storageblobprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-storageblobprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-storageblobprivateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-storageblobprivateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -2199,6 +2224,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `storageBlobPrivateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `storageBlobPrivateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. @@ -2338,6 +2370,7 @@ Configuration details for Purview Managed Storage Account queue private endpoint | [`name`](#parameter-storagequeueprivateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-storagequeueprivateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-storagequeueprivateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-storagequeueprivateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-storagequeueprivateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-storagequeueprivateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-storagequeueprivateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -2534,6 +2567,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `storageQueuePrivateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `storageQueuePrivateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/purview/account/main.bicep b/avm/res/purview/account/main.bicep index 42a4f887f9..b0beba41f6 100644 --- a/avm/res/purview/account/main.bicep +++ b/avm/res/purview/account/main.bicep @@ -558,6 +558,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + // Variant 1: A default service can be assumed (i.e., for services that only have one private endpoint type) @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/purview/account/main.json b/avm/res/purview/account/main.json index 79ece163df..b7c2504156 100644 --- a/avm/res/purview/account/main.json +++ b/avm/res/purview/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "10478276284688945005" + "templateHash": "17174301778360799299" }, "name": "Purview Accounts", "description": "This module deploys a Purview Account.", @@ -258,6 +258,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/recovery-services/vault/README.md b/avm/res/recovery-services/vault/README.md index 60cb4dddd3..445ea28d6f 100644 --- a/avm/res/recovery-services/vault/README.md +++ b/avm/res/recovery-services/vault/README.md @@ -2181,6 +2181,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -2377,6 +2378,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/recovery-services/vault/main.bicep b/avm/res/recovery-services/vault/main.bicep index 293e839c67..8e50e68716 100644 --- a/avm/res/recovery-services/vault/main.bicep +++ b/avm/res/recovery-services/vault/main.bicep @@ -444,6 +444,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/recovery-services/vault/main.json b/avm/res/recovery-services/vault/main.json index 42225dc535..577dc26077 100644 --- a/avm/res/recovery-services/vault/main.json +++ b/avm/res/recovery-services/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "7655373586022764690" + "templateHash": "2215975966866051299" }, "name": "Recovery Services Vaults", "description": "This module deploys a Recovery Services Vault.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/relay/namespace/README.md b/avm/res/relay/namespace/README.md index c9a6931fbf..00e11010ba 100644 --- a/avm/res/relay/namespace/README.md +++ b/avm/res/relay/namespace/README.md @@ -927,6 +927,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1123,6 +1124,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/relay/namespace/main.bicep b/avm/res/relay/namespace/main.bicep index 18ca612874..1152aa1dfc 100644 --- a/avm/res/relay/namespace/main.bicep +++ b/avm/res/relay/namespace/main.bicep @@ -381,6 +381,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/relay/namespace/main.json b/avm/res/relay/namespace/main.json index 20a1a87e84..bd8938cde5 100644 --- a/avm/res/relay/namespace/main.json +++ b/avm/res/relay/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "18413649405351363659" + "templateHash": "17233437629149406382" }, "name": "Relay Namespaces", "description": "This module deploys a Relay Namespace", @@ -123,6 +123,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/search/search-service/README.md b/avm/res/search/search-service/README.md index b2b9d3299b..fddc70b242 100644 --- a/avm/res/search/search-service/README.md +++ b/avm/res/search/search-service/README.md @@ -942,6 +942,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1138,6 +1139,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/search/search-service/main.bicep b/avm/res/search/search-service/main.bicep index 755034072f..3beb423d54 100644 --- a/avm/res/search/search-service/main.bicep +++ b/avm/res/search/search-service/main.bicep @@ -379,6 +379,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/search/search-service/main.json b/avm/res/search/search-service/main.json index 303cb2d214..df489a9cab 100644 --- a/avm/res/search/search-service/main.json +++ b/avm/res/search/search-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "13049058701753751229" + "templateHash": "13069544635575133650" }, "name": "Search Services", "description": "This module deploys a Search Service.", @@ -136,6 +136,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/service-bus/namespace/README.md b/avm/res/service-bus/namespace/README.md index 125cf687fa..4ef8712ce2 100644 --- a/avm/res/service-bus/namespace/README.md +++ b/avm/res/service-bus/namespace/README.md @@ -294,9 +294,11 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } } ] + name: 'myPrivateEndpoint' privateDnsZoneResourceIds: [ '' ] + privateLinkServiceConnectionName: 'customLinkName' subnetResourceId: '' tags: { Environment: 'Non-Prod' @@ -526,9 +528,11 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } } ], + "name": "myPrivateEndpoint", "privateDnsZoneResourceIds": [ "" ], + "privateLinkServiceConnectionName": "customLinkName", "subnetResourceId": "", "tags": { "Environment": "Non-Prod", @@ -1647,6 +1651,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1843,6 +1848,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index 8f3a7c22ab..66b8bc38c8 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -499,6 +499,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index e9e7ee32e7..86701b3773 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "12101686609428052773" + "templateHash": "9228932977984857525" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep b/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep index a1a500e934..b08e017836 100644 --- a/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep @@ -213,6 +213,8 @@ module testDeployment '../../../main.bicep' = [ ] privateEndpoints: [ { + name: 'myPrivateEndpoint' + privateLinkServiceConnectionName: 'customLinkName' subnetResourceId: nestedDependencies.outputs.subnetResourceId privateDnsZoneResourceIds: [ nestedDependencies.outputs.privateDNSZoneResourceId diff --git a/avm/res/signal-r-service/signal-r/README.md b/avm/res/signal-r-service/signal-r/README.md index 28819bb5be..9466ec00a2 100644 --- a/avm/res/signal-r-service/signal-r/README.md +++ b/avm/res/signal-r-service/signal-r/README.md @@ -695,6 +695,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -891,6 +892,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/signal-r-service/signal-r/main.bicep b/avm/res/signal-r-service/signal-r/main.bicep index 66afadbde1..a1ddf132b0 100644 --- a/avm/res/signal-r-service/signal-r/main.bicep +++ b/avm/res/signal-r-service/signal-r/main.bicep @@ -368,6 +368,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/signal-r-service/signal-r/main.json b/avm/res/signal-r-service/signal-r/main.json index fda5c481ab..6a3345bbfc 100644 --- a/avm/res/signal-r-service/signal-r/main.json +++ b/avm/res/signal-r-service/signal-r/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "3713977800764774886" + "templateHash": "15213574724705700339" }, "name": "SignalR Service SignalR", "description": "This module deploys a SignalR Service SignalR.", @@ -123,6 +123,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/signal-r-service/web-pub-sub/README.md b/avm/res/signal-r-service/web-pub-sub/README.md index 303f1a02c5..bd364eb46f 100644 --- a/avm/res/signal-r-service/web-pub-sub/README.md +++ b/avm/res/signal-r-service/web-pub-sub/README.md @@ -664,6 +664,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -860,6 +861,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/signal-r-service/web-pub-sub/main.bicep b/avm/res/signal-r-service/web-pub-sub/main.bicep index 7df5cef044..6f0d42ceb5 100644 --- a/avm/res/signal-r-service/web-pub-sub/main.bicep +++ b/avm/res/signal-r-service/web-pub-sub/main.bicep @@ -340,6 +340,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/signal-r-service/web-pub-sub/main.json b/avm/res/signal-r-service/web-pub-sub/main.json index b0cd717eea..bb5da794c7 100644 --- a/avm/res/signal-r-service/web-pub-sub/main.json +++ b/avm/res/signal-r-service/web-pub-sub/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "5123587026582880440" + "templateHash": "17706812200280440535" }, "name": "SignalR Web PubSub Services", "description": "This module deploys a SignalR Web PubSub Service.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/sql/server/README.md b/avm/res/sql/server/README.md index ed1f6f52c9..31d607a435 100644 --- a/avm/res/sql/server/README.md +++ b/avm/res/sql/server/README.md @@ -1251,6 +1251,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -1447,6 +1448,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 1b95eb7fb0..b068331ee2 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -529,6 +529,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index ef545418bd..5611ff656f 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "16217985022425971907" + "templateHash": "7285009334035421293" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -146,6 +146,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/synapse/private-link-hub/README.md b/avm/res/synapse/private-link-hub/README.md index 676a1ea168..0fe999a92c 100644 --- a/avm/res/synapse/private-link-hub/README.md +++ b/avm/res/synapse/private-link-hub/README.md @@ -420,6 +420,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | @@ -616,6 +617,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/synapse/private-link-hub/main.bicep b/avm/res/synapse/private-link-hub/main.bicep index c704bb0d20..f7294f8188 100644 --- a/avm/res/synapse/private-link-hub/main.bicep +++ b/avm/res/synapse/private-link-hub/main.bicep @@ -204,6 +204,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') service: string? diff --git a/avm/res/synapse/private-link-hub/main.json b/avm/res/synapse/private-link-hub/main.json index edf4acca71..fd84d2835d 100644 --- a/avm/res/synapse/private-link-hub/main.json +++ b/avm/res/synapse/private-link-hub/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "13337660618824697392" + "templateHash": "10734135538553335635" }, "name": "Azure Synapse Analytics", "description": "This module deploys an Azure Synapse Analytics (Private Link Hub).", @@ -123,6 +123,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "nullable": true, diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index e3b70632bd..62fbe376dd 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -1144,6 +1144,7 @@ Configuration details for private endpoints. For security reasons, it is recomme | [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | | [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided. | | [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | | [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | | [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | @@ -1346,6 +1347,13 @@ The private DNS zone groups to associate the private endpoint with. A DNS zone g - Required: No - Type: array +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + ### Parameter: `privateEndpoints.resourceGroupName` Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index cd2ebc4b68..1e9c17624b 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -451,6 +451,9 @@ type privateEndpointType = { @description('Optional. The location to deploy the private endpoint to.') location: string? + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') service: string diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index 4ae63838c2..89049892d8 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "229457556089965339" + "templateHash": "456170449467900103" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -138,6 +138,13 @@ "description": "Optional. The location to deploy the private endpoint to." } }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, "service": { "type": "string", "metadata": { From 1428397a83320e214b79395f615d9b859ff46d00 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:48:22 +0200 Subject: [PATCH 23/66] fix: update VM json, so static test doesn't fail (#1668) Update of VM main.json file --- avm/res/compute/virtual-machine/main.json | 68 +++++++++++------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/avm/res/compute/virtual-machine/main.json b/avm/res/compute/virtual-machine/main.json index 65a9cdeb1e..5d9c60bd68 100644 --- a/avm/res/compute/virtual-machine/main.json +++ b/avm/res/compute/virtual-machine/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "11337690191653715343" + "version": "0.26.54.24096", + "templateHash": "5410471245439722622" }, "name": "Virtual Machines", "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", @@ -684,7 +684,7 @@ "signedProtocol": "https" }, "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(if(parameters('extensionAadJoinConfig').enabled, true(), coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false())), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(if(parameters('extensionAadJoinConfig').enabled, true(), coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false())), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]", @@ -815,7 +815,7 @@ "proximityPlacementGroup": "[if(not(empty(parameters('proximityPlacementGroupResourceId'))), createObject('id', parameters('proximityPlacementGroupResourceId')), null())]", "priority": "[parameters('priority')]", "evictionPolicy": "[if(parameters('enableEvictionPolicy'), 'Deallocate', null())]", - "billingProfile": "[if(and(not(empty(parameters('priority'))), not(empty(parameters('maxPriceForLowPriorityVm')))), createObject('maxPrice', parameters('maxPriceForLowPriorityVm')), null())]", + "billingProfile": "[if(and(not(empty(parameters('priority'))), not(empty(parameters('maxPriceForLowPriorityVm')))), createObject('maxPrice', json(parameters('maxPriceForLowPriorityVm'))), null())]", "host": "[if(not(empty(parameters('dedicatedHostId'))), createObject('id', parameters('dedicatedHostId')), null())]", "licenseType": "[if(not(empty(parameters('licenseType'))), parameters('licenseType'), null())]" }, @@ -949,8 +949,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "4698911337326052062" + "version": "0.26.54.24096", + "templateHash": "13415879962041783892" } }, "definitions": { @@ -2419,8 +2419,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -2625,8 +2625,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -2827,8 +2827,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3041,8 +3041,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3239,8 +3239,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3436,8 +3436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3637,8 +3637,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3846,8 +3846,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4047,8 +4047,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4246,8 +4246,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4436,8 +4436,8 @@ "enableAutomaticUpgrade": "[if(contains(parameters('extensionHostPoolRegistration'), 'enableAutomaticUpgrade'), createObject('value', parameters('extensionHostPoolRegistration').enableAutomaticUpgrade), createObject('value', false()))]", "settings": { "value": { - "modulesUrl": "[parameters('extensionHostPoolRegistration').hostPoolModulesUrl]", - "configurationFunction": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'configurationFunction'), 'Configuration.ps1\\AddSessionHost')]", + "modulesUrl": "[parameters('extensionHostPoolRegistration').modulesUrl]", + "configurationFunction": "[parameters('extensionHostPoolRegistration').configurationFunction]", "properties": { "hostPoolName": "[parameters('extensionHostPoolRegistration').hostPoolName]", "registrationInfoToken": "[parameters('extensionHostPoolRegistration').registrationInfoToken]", @@ -4456,8 +4456,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4656,8 +4656,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.54.24096", + "templateHash": "16251967456691023698" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4855,8 +4855,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "15677187951825533891" + "version": "0.26.54.24096", + "templateHash": "5385249890312845255" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", From 5c3e9228b4cf4522623b5be746d8c63b27f3edfd Mon Sep 17 00:00:00 2001 From: John Date: Thu, 11 Apr 2024 18:09:35 +0200 Subject: [PATCH 24/66] feat: Added UDTs for Azure Firewall Application, network and NAT rules - `avm/res/network/azure-firewall` (#1440) ## Description This pull request adds UDTs for Azure Firewall application, network, and NAT rules. Also, added an extra network rule for service tags in the `max` test. @hundredacres @AlexanderSehr We must be aware that the change itself is not breaking, but when existing rules are not written as the UDT expects it will break a deployment. See the image below (left is how it is now and right is what's new with the UDT). How do we handle this version wise? image ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.azure-firewall](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=johnlokerse%2Fadd-udt-azfw)](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/network/azure-firewall/README.md | 615 ++++++++++++++++-- avm/res/network/azure-firewall/main.bicep | 225 +++++-- avm/res/network/azure-firewall/main.json | 453 ++++++++++++- .../tests/e2e/max/main.test.bicep | 36 +- .../tests/e2e/waf-aligned/dependencies.bicep | 3 + .../tests/e2e/waf-aligned/main.test.bicep | 20 +- avm/res/network/azure-firewall/version.json | 2 +- 7 files changed, 1238 insertions(+), 116 deletions(-) diff --git a/avm/res/network/azure-firewall/README.md b/avm/res/network/azure-firewall/README.md index 8629811336..da0018a179 100644 --- a/avm/res/network/azure-firewall/README.md +++ b/avm/res/network/azure-firewall/README.md @@ -444,7 +444,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-app-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -456,12 +456,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-ase-tags' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -472,12 +472,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-ase-management' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -515,7 +515,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-network-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -535,6 +535,22 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { '*' ] } + { + description: 'allow azure devops' + destinationAddresses: [ + 'AzureDevOps' + ] + destinationPorts: [ + '443' + ] + name: 'allow-azure-devops' + protocols: [ + 'Any' + ] + sourceAddresses: [ + '*' + ] + } ] } } @@ -595,7 +611,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-app-rules", "properties": { "action": { - "type": "allow" + "type": "Allow" }, "priority": 100, "rules": [ @@ -607,12 +623,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-ase-tags", "protocols": [ { - "port": "80", - "protocolType": "HTTP" + "port": 80, + "protocolType": "Http" }, { - "port": "443", - "protocolType": "HTTPS" + "port": 443, + "protocolType": "Https" } ], "sourceAddresses": [ @@ -623,12 +639,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-ase-management", "protocols": [ { - "port": "80", - "protocolType": "HTTP" + "port": 80, + "protocolType": "Http" }, { - "port": "443", - "protocolType": "HTTPS" + "port": 443, + "protocolType": "Https" } ], "sourceAddresses": [ @@ -674,7 +690,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-network-rules", "properties": { "action": { - "type": "allow" + "type": "Allow" }, "priority": 100, "rules": [ @@ -693,6 +709,22 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "sourceAddresses": [ "*" ] + }, + { + "description": "allow azure devops", + "destinationAddresses": [ + "AzureDevOps" + ], + "destinationPorts": [ + "443" + ], + "name": "allow-azure-devops", + "protocols": [ + "Any" + ], + "sourceAddresses": [ + "*" + ] } ] } @@ -766,7 +798,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-app-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -778,12 +810,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-ase-tags' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -794,12 +826,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-ase-management' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -833,7 +865,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { name: 'allow-network-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -896,7 +928,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-app-rules", "properties": { "action": { - "type": "allow" + "type": "Allow" }, "priority": 100, "rules": [ @@ -908,12 +940,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-ase-tags", "protocols": [ { - "port": "80", - "protocolType": "HTTP" + "port": 80, + "protocolType": "Http" }, { - "port": "443", - "protocolType": "HTTPS" + "port": 443, + "protocolType": "Https" } ], "sourceAddresses": [ @@ -924,12 +956,12 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-ase-management", "protocols": [ { - "port": "80", - "protocolType": "HTTP" + "port": 80, + "protocolType": "Http" }, { - "port": "443", - "protocolType": "HTTPS" + "port": 443, + "protocolType": "Https" } ], "sourceAddresses": [ @@ -969,7 +1001,7 @@ module azureFirewall 'br/public:avm/res/network/azure-firewall:' = { "name": "allow-network-rules", "properties": { "action": { - "type": "allow" + "type": "Allow" }, "priority": 100, "rules": [ @@ -1106,7 +1138,176 @@ Collection of application rule collections used by Azure Firewall. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-applicationrulecollectionsname) | string | Name of the application rule collection. | +| [`properties`](#parameter-applicationrulecollectionsproperties) | object | Properties of the azure firewall application rule collection. | + +### Parameter: `applicationRuleCollections.name` + +Name of the application rule collection. + +- Required: Yes +- Type: string + +### Parameter: `applicationRuleCollections.properties` + +Properties of the azure firewall application rule collection. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`action`](#parameter-applicationrulecollectionspropertiesaction) | object | The action type of a rule collection. | +| [`priority`](#parameter-applicationrulecollectionspropertiespriority) | int | Priority of the application rule collection. | +| [`rules`](#parameter-applicationrulecollectionspropertiesrules) | array | Collection of rules used by a application rule collection. | + +### Parameter: `applicationRuleCollections.properties.action` + +The action type of a rule collection. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`type`](#parameter-applicationrulecollectionspropertiesactiontype) | string | The type of action. | + +### Parameter: `applicationRuleCollections.properties.action.type` + +The type of action. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Deny' + ] + ``` + +### Parameter: `applicationRuleCollections.properties.priority` + +Priority of the application rule collection. + +- Required: Yes +- Type: int + +### Parameter: `applicationRuleCollections.properties.rules` + +Collection of rules used by a application rule collection. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-applicationrulecollectionspropertiesrulesname) | string | Name of the application rule. | +| [`protocols`](#parameter-applicationrulecollectionspropertiesrulesprotocols) | array | Array of ApplicationRuleProtocols. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-applicationrulecollectionspropertiesrulesdescription) | string | Description of the rule. | +| [`fqdnTags`](#parameter-applicationrulecollectionspropertiesrulesfqdntags) | array | List of FQDN Tags for this rule. | +| [`sourceAddresses`](#parameter-applicationrulecollectionspropertiesrulessourceaddresses) | array | List of source IP addresses for this rule. | +| [`sourceIpGroups`](#parameter-applicationrulecollectionspropertiesrulessourceipgroups) | array | List of source IpGroups for this rule. | +| [`targetFqdns`](#parameter-applicationrulecollectionspropertiesrulestargetfqdns) | array | List of FQDNs for this rule. | + +### Parameter: `applicationRuleCollections.properties.rules.name` + +Name of the application rule. + +- Required: Yes +- Type: string + +### Parameter: `applicationRuleCollections.properties.rules.protocols` + +Array of ApplicationRuleProtocols. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`protocolType`](#parameter-applicationrulecollectionspropertiesrulesprotocolsprotocoltype) | string | Protocol type. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`port`](#parameter-applicationrulecollectionspropertiesrulesprotocolsport) | int | Port number for the protocol. | + +### Parameter: `applicationRuleCollections.properties.rules.protocols.protocolType` + +Protocol type. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Http' + 'Https' + 'Mssql' + ] + ``` + +### Parameter: `applicationRuleCollections.properties.rules.protocols.port` + +Port number for the protocol. + +- Required: No +- Type: int + +### Parameter: `applicationRuleCollections.properties.rules.description` + +Description of the rule. + +- Required: No +- Type: string + +### Parameter: `applicationRuleCollections.properties.rules.fqdnTags` + +List of FQDN Tags for this rule. + +- Required: No +- Type: array + +### Parameter: `applicationRuleCollections.properties.rules.sourceAddresses` + +List of source IP addresses for this rule. + +- Required: No +- Type: array + +### Parameter: `applicationRuleCollections.properties.rules.sourceIpGroups` + +List of source IpGroups for this rule. + +- Required: No +- Type: array + +### Parameter: `applicationRuleCollections.properties.rules.targetFqdns` + +List of FQDNs for this rule. + +- Required: No +- Type: array ### Parameter: `azureSkuTier` @@ -1352,7 +1553,175 @@ Collection of NAT rule collections used by Azure Firewall. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-natrulecollectionsname) | string | Name of the NAT rule collection. | +| [`properties`](#parameter-natrulecollectionsproperties) | object | Properties of the azure firewall NAT rule collection. | + +### Parameter: `natRuleCollections.name` + +Name of the NAT rule collection. + +- Required: Yes +- Type: string + +### Parameter: `natRuleCollections.properties` + +Properties of the azure firewall NAT rule collection. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`action`](#parameter-natrulecollectionspropertiesaction) | object | The action type of a NAT rule collection. | +| [`priority`](#parameter-natrulecollectionspropertiespriority) | int | Priority of the NAT rule collection. | +| [`rules`](#parameter-natrulecollectionspropertiesrules) | array | Collection of rules used by a NAT rule collection. | + +### Parameter: `natRuleCollections.properties.action` + +The action type of a NAT rule collection. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`type`](#parameter-natrulecollectionspropertiesactiontype) | string | The type of action. | + +### Parameter: `natRuleCollections.properties.action.type` + +The type of action. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Dnat' + 'Snat' + ] + ``` + +### Parameter: `natRuleCollections.properties.priority` + +Priority of the NAT rule collection. + +- Required: Yes +- Type: int + +### Parameter: `natRuleCollections.properties.rules` + +Collection of rules used by a NAT rule collection. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-natrulecollectionspropertiesrulesname) | string | Name of the NAT rule. | +| [`protocols`](#parameter-natrulecollectionspropertiesrulesprotocols) | array | Array of AzureFirewallNetworkRuleProtocols applicable to this NAT rule. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-natrulecollectionspropertiesrulesdescription) | string | Description of the rule. | +| [`destinationAddresses`](#parameter-natrulecollectionspropertiesrulesdestinationaddresses) | array | List of destination IP addresses for this rule. Supports IP ranges, prefixes, and service tags. | +| [`destinationPorts`](#parameter-natrulecollectionspropertiesrulesdestinationports) | array | List of destination ports. | +| [`sourceAddresses`](#parameter-natrulecollectionspropertiesrulessourceaddresses) | array | List of source IP addresses for this rule. | +| [`sourceIpGroups`](#parameter-natrulecollectionspropertiesrulessourceipgroups) | array | List of source IpGroups for this rule. | +| [`translatedAddress`](#parameter-natrulecollectionspropertiesrulestranslatedaddress) | string | The translated address for this NAT rule. | +| [`translatedFqdn`](#parameter-natrulecollectionspropertiesrulestranslatedfqdn) | string | The translated FQDN for this NAT rule. | +| [`translatedPort`](#parameter-natrulecollectionspropertiesrulestranslatedport) | string | The translated port for this NAT rule. | + +### Parameter: `natRuleCollections.properties.rules.name` + +Name of the NAT rule. + +- Required: Yes +- Type: string + +### Parameter: `natRuleCollections.properties.rules.protocols` + +Array of AzureFirewallNetworkRuleProtocols applicable to this NAT rule. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'Any' + 'ICMP' + 'TCP' + 'UDP' + ] + ``` + +### Parameter: `natRuleCollections.properties.rules.description` + +Description of the rule. + +- Required: No +- Type: string + +### Parameter: `natRuleCollections.properties.rules.destinationAddresses` + +List of destination IP addresses for this rule. Supports IP ranges, prefixes, and service tags. + +- Required: No +- Type: array + +### Parameter: `natRuleCollections.properties.rules.destinationPorts` + +List of destination ports. + +- Required: No +- Type: array + +### Parameter: `natRuleCollections.properties.rules.sourceAddresses` + +List of source IP addresses for this rule. + +- Required: No +- Type: array + +### Parameter: `natRuleCollections.properties.rules.sourceIpGroups` + +List of source IpGroups for this rule. + +- Required: No +- Type: array + +### Parameter: `natRuleCollections.properties.rules.translatedAddress` + +The translated address for this NAT rule. + +- Required: No +- Type: string + +### Parameter: `natRuleCollections.properties.rules.translatedFqdn` + +The translated FQDN for this NAT rule. + +- Required: No +- Type: string + +### Parameter: `natRuleCollections.properties.rules.translatedPort` + +The translated port for this NAT rule. + +- Required: No +- Type: string ### Parameter: `networkRuleCollections` @@ -1360,7 +1729,167 @@ Collection of network rule collections used by Azure Firewall. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-networkrulecollectionsname) | string | Name of the network rule collection. | +| [`properties`](#parameter-networkrulecollectionsproperties) | object | Properties of the azure firewall network rule collection. | + +### Parameter: `networkRuleCollections.name` + +Name of the network rule collection. + +- Required: Yes +- Type: string + +### Parameter: `networkRuleCollections.properties` + +Properties of the azure firewall network rule collection. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`action`](#parameter-networkrulecollectionspropertiesaction) | object | The action type of a rule collection. | +| [`priority`](#parameter-networkrulecollectionspropertiespriority) | int | Priority of the network rule collection. | +| [`rules`](#parameter-networkrulecollectionspropertiesrules) | array | Collection of rules used by a network rule collection. | + +### Parameter: `networkRuleCollections.properties.action` + +The action type of a rule collection. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`type`](#parameter-networkrulecollectionspropertiesactiontype) | string | The type of action. | + +### Parameter: `networkRuleCollections.properties.action.type` + +The type of action. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Deny' + ] + ``` + +### Parameter: `networkRuleCollections.properties.priority` + +Priority of the network rule collection. + +- Required: Yes +- Type: int + +### Parameter: `networkRuleCollections.properties.rules` + +Collection of rules used by a network rule collection. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-networkrulecollectionspropertiesrulesname) | string | Name of the network rule. | +| [`protocols`](#parameter-networkrulecollectionspropertiesrulesprotocols) | array | Array of AzureFirewallNetworkRuleProtocols. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-networkrulecollectionspropertiesrulesdescription) | string | Description of the rule. | +| [`destinationAddresses`](#parameter-networkrulecollectionspropertiesrulesdestinationaddresses) | array | List of destination IP addresses. | +| [`destinationFqdns`](#parameter-networkrulecollectionspropertiesrulesdestinationfqdns) | array | List of destination FQDNs. | +| [`destinationIpGroups`](#parameter-networkrulecollectionspropertiesrulesdestinationipgroups) | array | List of destination IP groups for this rule. | +| [`destinationPorts`](#parameter-networkrulecollectionspropertiesrulesdestinationports) | array | List of destination ports. | +| [`sourceAddresses`](#parameter-networkrulecollectionspropertiesrulessourceaddresses) | array | List of source IP addresses for this rule. | +| [`sourceIpGroups`](#parameter-networkrulecollectionspropertiesrulessourceipgroups) | array | List of source IpGroups for this rule. | + +### Parameter: `networkRuleCollections.properties.rules.name` + +Name of the network rule. + +- Required: Yes +- Type: string + +### Parameter: `networkRuleCollections.properties.rules.protocols` + +Array of AzureFirewallNetworkRuleProtocols. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'Any' + 'ICMP' + 'TCP' + 'UDP' + ] + ``` + +### Parameter: `networkRuleCollections.properties.rules.description` + +Description of the rule. + +- Required: No +- Type: string + +### Parameter: `networkRuleCollections.properties.rules.destinationAddresses` + +List of destination IP addresses. + +- Required: No +- Type: array + +### Parameter: `networkRuleCollections.properties.rules.destinationFqdns` + +List of destination FQDNs. + +- Required: No +- Type: array + +### Parameter: `networkRuleCollections.properties.rules.destinationIpGroups` + +List of destination IP groups for this rule. + +- Required: No +- Type: array + +### Parameter: `networkRuleCollections.properties.rules.destinationPorts` + +List of destination ports. + +- Required: No +- Type: array + +### Parameter: `networkRuleCollections.properties.rules.sourceAddresses` + +List of source IP addresses for this rule. + +- Required: No +- Type: array + +### Parameter: `networkRuleCollections.properties.rules.sourceIpGroups` + +List of source IpGroups for this rule. + +- Required: No +- Type: array ### Parameter: `publicIPAddressObject` diff --git a/avm/res/network/azure-firewall/main.bicep b/avm/res/network/azure-firewall/main.bicep index 08f4af0cc2..3176534913 100644 --- a/avm/res/network/azure-firewall/main.bicep +++ b/avm/res/network/azure-firewall/main.bicep @@ -34,13 +34,13 @@ param managementIPResourceID string = '' param managementIPAddressObject object = {} @description('Optional. Collection of application rule collections used by Azure Firewall.') -param applicationRuleCollections array = [] +param applicationRuleCollections applicationRuleCollectionType @description('Optional. Collection of network rule collections used by Azure Firewall.') -param networkRuleCollections array = [] +param networkRuleCollections networkRuleCollectionType @description('Optional. Collection of NAT rule collections used by Azure Firewall.') -param natRuleCollections array = [] +param natRuleCollections natRuleCollectionType @description('Optional. Resource ID of the Firewall Policy that should be attached.') param firewallPolicyId string = '' @@ -261,41 +261,33 @@ resource azureFirewall 'Microsoft.Network/azureFirewalls@2023-04-01' = { location: location zones: length(zones) == 0 ? null : zones tags: tags - properties: azureSkuName == 'AZFW_VNet' - ? { - threatIntelMode: threatIntelMode - firewallPolicy: !empty(firewallPolicyId) - ? { - id: firewallPolicyId - } - : null - ipConfigurations: ipConfigurations - managementIpConfiguration: requiresManagementIp ? managementIPConfiguration : null - sku: { - name: azureSkuName - tier: azureSkuTier - } - applicationRuleCollections: applicationRuleCollections - natRuleCollections: natRuleCollections - networkRuleCollections: networkRuleCollections - } - : { - firewallPolicy: !empty(firewallPolicyId) - ? { - id: firewallPolicyId - } - : null - sku: { - name: azureSkuName - tier: azureSkuTier - } - hubIPAddresses: !empty(hubIPAddresses) ? hubIPAddresses : null - virtualHub: !empty(virtualHubId) - ? { - id: virtualHubId - } - : null - } + properties: azureSkuName == 'AZFW_VNet' ? { + threatIntelMode: threatIntelMode + firewallPolicy: !empty(firewallPolicyId) ? { + id: firewallPolicyId + } : null + ipConfigurations: ipConfigurations + managementIpConfiguration: requiresManagementIp ? managementIPConfiguration : null + sku: { + name: azureSkuName + tier: azureSkuTier + } + applicationRuleCollections: applicationRuleCollections ?? [] + natRuleCollections: natRuleCollections ?? [] + networkRuleCollections: networkRuleCollections ?? [] + } : { + firewallPolicy: !empty(firewallPolicyId) ? { + id: firewallPolicyId + } : null + sku: { + name: azureSkuName + tier: azureSkuTier + } + hubIPAddresses: !empty(hubIPAddresses) ? hubIPAddresses : null + virtualHub: !empty(virtualHubId) ? { + id: virtualHubId + } : null + } } resource azureFirewall_lock 'Microsoft.Authorization/locks@2020-05-01' = @@ -379,13 +371,13 @@ output ipConfAzureFirewallSubnet object = contains(azureFirewall.properties, 'ip : {} @description('List of Application Rule Collections.') -output applicationRuleCollections array = applicationRuleCollections +output applicationRuleCollections array = applicationRuleCollections ?? [] @description('List of Network Rule Collections.') -output networkRuleCollections array = networkRuleCollections +output networkRuleCollections array = networkRuleCollections ?? [] @description('Collection of NAT rule collections used by Azure Firewall.') -output natRuleCollections array = natRuleCollections +output natRuleCollections array = natRuleCollections ?? [] @description('The location the resource was deployed into.') output location string = azureFirewall.location @@ -468,3 +460,154 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type natRuleCollectionType = { + @description('Required. Name of the NAT rule collection.') + name: string + + @description('Required. Properties of the azure firewall NAT rule collection.') + properties: { + @description('Required. The action type of a NAT rule collection.') + action: { + @description('Required. The type of action.') + type: 'Dnat' | 'Snat' + } + + @description('Required. Priority of the NAT rule collection.') + @minValue(100) + @maxValue(65000) + priority: int + + @description('Required. Collection of rules used by a NAT rule collection.') + rules: { + @description('Required. Name of the NAT rule.') + name: string + + @description('Optional. Description of the rule.') + description: string? + + @description('Required. Array of AzureFirewallNetworkRuleProtocols applicable to this NAT rule.') + protocols: ('TCP' | 'UDP' | 'Any' | 'ICMP')[] + + @description('Optional. List of destination IP addresses for this rule. Supports IP ranges, prefixes, and service tags.') + destinationAddresses: string[]? + + @description('Optional. List of destination ports.') + destinationPorts: string[]? + + @description('Optional. List of source IP addresses for this rule.') + sourceAddresses: string[]? + + @description('Optional. List of source IpGroups for this rule.') + sourceIpGroups: string[]? + + @description('Optional. The translated address for this NAT rule.') + translatedAddress: string? + + @description('Optional. The translated FQDN for this NAT rule.') + translatedFqdn: string? + + @description('Optional. The translated port for this NAT rule.') + translatedPort: string? + }[] + } +}[]? + +type applicationRuleCollectionType = { + @description('Required. Name of the application rule collection.') + name: string + + @description('Required. Properties of the azure firewall application rule collection.') + properties: { + @description('Required. The action type of a rule collection.') + action: { + @description('Required. The type of action.') + type: 'Allow' | 'Deny' + } + + @description('Required. Priority of the application rule collection.') + @minValue(100) + @maxValue(65000) + priority: int + + @description('Required. Collection of rules used by a application rule collection.') + rules: { + @description('Required. Name of the application rule.') + name: string + + @description('Optional. Description of the rule.') + description: string? + + @description('Required. Array of ApplicationRuleProtocols.') + protocols: { + @description('Optional. Port number for the protocol.') + @maxValue(64000) + port: int? + + @description('Required. Protocol type.') + protocolType: 'Http' | 'Https' | 'Mssql' + }[] + + @description('Optional. List of FQDN Tags for this rule.') + fqdnTags: string[]? + + @description('Optional. List of FQDNs for this rule.') + targetFqdns: string[]? + + @description('Optional. List of source IP addresses for this rule.') + sourceAddresses: string[]? + + @description('Optional. List of source IpGroups for this rule.') + sourceIpGroups: string[]? + }[] + } +}[]? + +type networkRuleCollectionType = { + @description('Required. Name of the network rule collection.') + name: string + + @description('Required. Properties of the azure firewall network rule collection.') + properties: { + @description('Required. The action type of a rule collection.') + action: { + @description('Required. The type of action.') + type: 'Allow' | 'Deny' + } + + @description('Required. Priority of the network rule collection.') + @minValue(100) + @maxValue(65000) + priority: int + + @description('Required. Collection of rules used by a network rule collection.') + rules: { + @description('Required. Name of the network rule.') + name: string + + @description('Optional. Description of the rule.') + description: string? + + @description('Required. Array of AzureFirewallNetworkRuleProtocols.') + protocols: ('TCP' | 'UDP' | 'Any' | 'ICMP')[] + + @description('Optional. List of destination IP addresses.') + destinationAddresses: string[]? + + @description('Optional. List of destination FQDNs.') + destinationFqdns: string[]? + + @description('Optional. List of destination IP groups for this rule.') + destinationIpGroups: string[]? + + @description('Optional. List of destination ports.') + destinationPorts: string[]? + + @description('Optional. List of source IP addresses for this rule.') + sourceAddresses: string[]? + + @description('Optional. List of source IpGroups for this rule.') + sourceIpGroups: string[]? + }[] + } +}[]? diff --git a/avm/res/network/azure-firewall/main.json b/avm/res/network/azure-firewall/main.json index b38f878a9d..877e4dbea2 100644 --- a/avm/res/network/azure-firewall/main.json +++ b/avm/res/network/azure-firewall/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "1527004517287633679" + "templateHash": "3831870271484638637" }, "name": "Azure Firewalls", "description": "This module deploys an Azure Firewall.", @@ -223,6 +223,440 @@ } }, "nullable": true + }, + "natRuleCollectionType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the NAT rule collection." + } + }, + "properties": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Dnat", + "Snat" + ], + "metadata": { + "description": "Required. The type of action." + } + } + }, + "metadata": { + "description": "Required. The action type of a NAT rule collection." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 65000, + "metadata": { + "description": "Required. Priority of the NAT rule collection." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the NAT rule." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the rule." + } + }, + "protocols": { + "type": "array", + "allowedValues": [ + "Any", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. Array of AzureFirewallNetworkRuleProtocols applicable to this NAT rule." + } + }, + "destinationAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination IP addresses for this rule. Supports IP ranges, prefixes, and service tags." + } + }, + "destinationPorts": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination ports." + } + }, + "sourceAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IP addresses for this rule." + } + }, + "sourceIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IpGroups for this rule." + } + }, + "translatedAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The translated address for this NAT rule." + } + }, + "translatedFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The translated FQDN for this NAT rule." + } + }, + "translatedPort": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The translated port for this NAT rule." + } + } + } + }, + "metadata": { + "description": "Required. Collection of rules used by a NAT rule collection." + } + } + }, + "metadata": { + "description": "Required. Properties of the azure firewall NAT rule collection." + } + } + } + }, + "nullable": true + }, + "applicationRuleCollectionType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the application rule collection." + } + }, + "properties": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. The type of action." + } + } + }, + "metadata": { + "description": "Required. The action type of a rule collection." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 65000, + "metadata": { + "description": "Required. Priority of the application rule collection." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the application rule." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the rule." + } + }, + "protocols": { + "type": "array", + "items": { + "type": "object", + "properties": { + "port": { + "type": "int", + "nullable": true, + "maxValue": 64000, + "metadata": { + "description": "Optional. Port number for the protocol." + } + }, + "protocolType": { + "type": "string", + "allowedValues": [ + "Http", + "Https", + "Mssql" + ], + "metadata": { + "description": "Required. Protocol type." + } + } + } + }, + "metadata": { + "description": "Required. Array of ApplicationRuleProtocols." + } + }, + "fqdnTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of FQDN Tags for this rule." + } + }, + "targetFqdns": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of FQDNs for this rule." + } + }, + "sourceAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IP addresses for this rule." + } + }, + "sourceIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IpGroups for this rule." + } + } + } + }, + "metadata": { + "description": "Required. Collection of rules used by a application rule collection." + } + } + }, + "metadata": { + "description": "Required. Properties of the azure firewall application rule collection." + } + } + } + }, + "nullable": true + }, + "networkRuleCollectionType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the network rule collection." + } + }, + "properties": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. The type of action." + } + } + }, + "metadata": { + "description": "Required. The action type of a rule collection." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 65000, + "metadata": { + "description": "Required. Priority of the network rule collection." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the network rule." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the rule." + } + }, + "protocols": { + "type": "array", + "allowedValues": [ + "Any", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. Array of AzureFirewallNetworkRuleProtocols." + } + }, + "destinationAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination IP addresses." + } + }, + "destinationFqdns": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination FQDNs." + } + }, + "destinationIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination IP groups for this rule." + } + }, + "destinationPorts": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination ports." + } + }, + "sourceAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IP addresses for this rule." + } + }, + "sourceIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IpGroups for this rule." + } + } + } + }, + "metadata": { + "description": "Required. Collection of rules used by a network rule collection." + } + } + }, + "metadata": { + "description": "Required. Properties of the azure firewall network rule collection." + } + } + } + }, + "nullable": true } }, "parameters": { @@ -289,22 +723,19 @@ } }, "applicationRuleCollections": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/applicationRuleCollectionType", "metadata": { "description": "Optional. Collection of application rule collections used by Azure Firewall." } }, "networkRuleCollections": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/networkRuleCollectionType", "metadata": { "description": "Optional. Collection of network rule collections used by Azure Firewall." } }, "natRuleCollections": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/natRuleCollectionType", "metadata": { "description": "Optional. Collection of NAT rule collections used by Azure Firewall." } @@ -445,7 +876,7 @@ "location": "[parameters('location')]", "zones": "[if(equals(length(parameters('zones')), 0), null(), parameters('zones'))]", "tags": "[parameters('tags')]", - "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('managementIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value))), createObject()))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', parameters('applicationRuleCollections'), 'natRuleCollections', parameters('natRuleCollections'), 'networkRuleCollections', parameters('networkRuleCollections')), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]", + "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('managementIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value))), createObject()))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', coalesce(parameters('applicationRuleCollections'), createArray()), 'natRuleCollections', coalesce(parameters('natRuleCollections'), createArray()), 'networkRuleCollections', coalesce(parameters('networkRuleCollections'), createArray())), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]", "dependsOn": [ "managementIPAddress", "publicIPAddress" @@ -1757,21 +2188,21 @@ "metadata": { "description": "List of Application Rule Collections." }, - "value": "[parameters('applicationRuleCollections')]" + "value": "[coalesce(parameters('applicationRuleCollections'), createArray())]" }, "networkRuleCollections": { "type": "array", "metadata": { "description": "List of Network Rule Collections." }, - "value": "[parameters('networkRuleCollections')]" + "value": "[coalesce(parameters('networkRuleCollections'), createArray())]" }, "natRuleCollections": { "type": "array", "metadata": { "description": "Collection of NAT rule collections used by Azure Firewall." }, - "value": "[parameters('natRuleCollections')]" + "value": "[coalesce(parameters('natRuleCollections'), createArray())]" }, "location": { "type": "string", diff --git a/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep b/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep index 7a69b2eebf..07ce848092 100644 --- a/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/max/main.test.bicep @@ -74,7 +74,7 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-app-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -86,12 +86,12 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-ase-tags' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -102,12 +102,12 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-ase-management' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -145,7 +145,7 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-network-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -165,6 +165,22 @@ module testDeployment '../../../main.bicep' = [ '*' ] } + { + name: 'allow-azure-devops' + protocols: [ + 'Any' + ] + description: 'allow azure devops' + sourceAddresses: [ + '*' + ] + destinationAddresses: [ + 'AzureDevOps' + ] + destinationPorts: [ + '443' + ] + } ] } } diff --git a/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep index de9bfec4ea..4e6ccd3d21 100644 --- a/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/waf-aligned/dependencies.bicep @@ -60,5 +60,8 @@ output virtualNetworkResourceId string = virtualNetwork.id @description('The resource ID of the created Public IP.') output publicIPResourceId string = publicIP.id +@description('The public IP Address of the created Public IP.') +output publicIPAddress string = publicIP.properties.ipAddress + @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep index ca84384701..33bd48b938 100644 --- a/avm/res/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/azure-firewall/tests/e2e/waf-aligned/main.test.bicep @@ -74,7 +74,7 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-app-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ @@ -86,12 +86,12 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-ase-tags' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -102,12 +102,12 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-ase-management' protocols: [ { - port: '80' - protocolType: 'HTTP' + port: 80 + protocolType: 'Http' } { - port: '443' - protocolType: 'HTTPS' + port: 443 + protocolType: 'Https' } ] sourceAddresses: [ @@ -141,7 +141,7 @@ module testDeployment '../../../main.bicep' = [ name: 'allow-network-rules' properties: { action: { - type: 'allow' + type: 'Allow' } priority: 100 rules: [ diff --git a/avm/res/network/azure-firewall/version.json b/avm/res/network/azure-firewall/version.json index 83083db694..1c035df49f 100644 --- a/avm/res/network/azure-firewall/version.json +++ b/avm/res/network/azure-firewall/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] From 54eb21648f9f220c4ef6ef5094e3c2307724e240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Fri, 12 Apr 2024 10:49:53 +0200 Subject: [PATCH 25/66] fix: aad-ds tests (#1669) ## Description Fixes the tests for the Domain Services Module ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.aad.domain-service](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml/badge.svg?branch=aad-ds-test-fix)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- avm/res/aad/domain-service/README.md | 4 ++-- avm/res/aad/domain-service/main.json | 4 ++-- .../aad/domain-service/tests/e2e/waf-aligned/main.test.bicep | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index 640ab0847b..87f33b1d5f 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -56,7 +56,7 @@ module domainService 'br/public:avm/res/aad/domain-service:' = { eventHubName: '' logCategoriesAndGroups: [ { - category: 'AllLogs' + categoryGroup: 'allLogs' } ] metricCategories: [ @@ -124,7 +124,7 @@ module domainService 'br/public:avm/res/aad/domain-service:' = { "eventHubName": "", "logCategoriesAndGroups": [ { - "category": "AllLogs" + "categoryGroup": "allLogs" } ], "metricCategories": [ diff --git a/avm/res/aad/domain-service/main.json b/avm/res/aad/domain-service/main.json index 84a4f00d14..c210fd41ff 100644 --- a/avm/res/aad/domain-service/main.json +++ b/avm/res/aad/domain-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "7386947428934165964" + "version": "0.26.54.24096", + "templateHash": "10683253750371964167" }, "name": "Azure Active Directory Domain Services", "description": "This module deploys an Azure Active Directory Domain Services (AADDS).", diff --git a/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep index e4af67fbc9..90db155b23 100644 --- a/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/aad/domain-service/tests/e2e/waf-aligned/main.test.bicep @@ -87,7 +87,7 @@ module testDeployment '../../../main.bicep' = { ] logCategoriesAndGroups: [ { - category: 'AllLogs' + categoryGroup: 'allLogs' } ] storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId @@ -155,7 +155,7 @@ module testDeploymentIdem '../../../main.bicep' = { ] logCategoriesAndGroups: [ { - category: 'AllLogs' + categoryGroup: 'allLogs' } ] storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId From bed50a4f961da33726e81efbc35b3a032ef65b4c Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:49:10 +0200 Subject: [PATCH 26/66] feat: Add additional logic if workflow fails and issue is created (#1403) More fine grained logic, regarding commenting and assigning new failing workflow issues --- .../avm.platform.manage-workflow-issue.yml | 13 +++- ...form.set-avm-github-issue-owner-config.yml | 16 +++-- .../Set-AvmGithubIssueForWorkflow.ps1 | 54 ++++++++++++++++- .../helper/Add-GithubIssueToProject.ps1 | 57 ++++++++++++++++++ .../platform/helper/Get-AvmCsvData.ps1 | 59 +++++++++++++++++++ 5 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 create mode 100644 avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/avm.platform.manage-workflow-issue.yml index 8d37f57d3d..9c80793d18 100644 --- a/.github/workflows/avm.platform.manage-workflow-issue.yml +++ b/.github/workflows/avm.platform.manage-workflow-issue.yml @@ -3,6 +3,7 @@ name: "avm.platform.manage-workflow-issue" on: schedule: - cron: "30 5 * * *" # Every day at 5:30 am + workflow_dispatch: jobs: manage-issues: @@ -14,16 +15,22 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - env: - GH_TOKEN: ${{ github.token }} - name: Manage issues + - uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Manage issues shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} run: | # Load used functions . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGithubIssueForWorkflow.ps1') $functionInput = @{ Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + RepoRoot = $env:GITHUB_WORKSPACE LimitNumberOfRuns = 500 LimitInDays = 2 IgnoreWorkflows = @() diff --git a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml index 775d83c94d..48cc265747 100644 --- a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml +++ b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml @@ -16,16 +16,22 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - env: - GH_TOKEN: ${{ github.token }} - name: "Run scripts" + - uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.TEAM_LINTER_APP_ID }} + private_key: ${{ secrets.TEAM_LINTER_PRIVATE_KEY }} + - name: "Run scripts" shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} run: | # Load used functions - . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGitHubIssueOwnerConfig.ps1') + . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Set-AvmGitHubIssueOwnerConfig.ps1') $functionInput = @{ - Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + RepoRoot = $env:GITHUB_WORKSPACE IssueUrl = "${{ github.event.issue.url }}" } diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index 7a30bfa96d..a2c1a5f98d 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -6,7 +6,10 @@ Check for failing pipelines and create issues for those, that are failing. If a pipeline fails, a new issue will be created, with a link to the failed pipeline. If the issue is already existing, a comment will be added, if a new run failed (with the link for the new failed run). If a pipeline run succeeds and an issue is open for the failed run, it will be closed (and a link to the successful run is added to the issue). .PARAMETER Repo -Mandatory. The name of the respository to scan. Needs to have the structure "/" +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER RepoRoot +Optional. Path to the root of the repository. .PARAMETER LimitNumberOfRuns Optional. Number of recent runs to scan for failed runs. Default is 100. @@ -23,7 +26,7 @@ Set-AvmGithubIssueForWorkflow -Repo 'owner/repo01' -LimitNumberOfRuns 100 -Limit Check the last 100 workflow runs in the repository 'owner/repo01' that happened in the last 2 days. If the workflow name is 'Pipeline 01', then ignore the workflow run. .NOTES -The function requires GitHub CLI to be installed. +Will be triggered by the workflow avm.platform.manage-workflow-issue.yml #> function Set-AvmGithubIssueForWorkflow { [CmdletBinding(SupportsShouldProcess)] @@ -31,6 +34,9 @@ function Set-AvmGithubIssueForWorkflow { [Parameter(Mandatory = $true)] [string] $Repo, + [Parameter(Mandatory = $false)] + [string] $RepoRoot = (Get-Item -Path $PSScriptRoot).parent.parent.parent.parent.FullName, + [Parameter(Mandatory = $false)] [int] $LimitNumberOfRuns = 100, @@ -41,6 +47,10 @@ function Set-AvmGithubIssueForWorkflow { [String[]] $IgnoreWorkflows = @() ) + # Loading helper functions + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Add-GithubIssueToProject.ps1') + $issues = gh issue list --state open --limit 500 --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --json 'title,url,body,comments,labels' --repo $Repo | ConvertFrom-Json -Depth 100 $runs = gh run list --json 'url,workflowName,headBranch,startedAt' --limit $LimitNumberOfRuns --repo $Repo | ConvertFrom-Json -Depth 100 $workflowRuns = @{} @@ -81,7 +91,45 @@ function Set-AvmGithubIssueForWorkflow { if ($issues.title -notcontains $issueName) { if ($PSCmdlet.ShouldProcess("Issue [$issueName]", 'Create')) { - gh issue create --title "$issueName" --body "$failedrun" --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --repo $Repo + $issueUrl = gh issue create --title "$issueName" --body "$failedrun" --label 'Type: AVM :a: :v: :m:,Type: Bug :bug:' --repo $Repo + $ProjectNumber = 538 # AVM - Issue Triage + $comment = @" +> [!IMPORTANT] +> @Azure/avm-core-team-technical-bicep, the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. +"@ + + if ($workflowRun.workflowName -match 'avm.(?:res|ptn)') { + $moduleName = $workflowRun.workflowName.Replace('.', '/') + $moduleIndex = $moduleName.StartsWith('avm/res') ? 'Bicep-Resource' : 'Bicep-Pattern' + # get CSV data + $module = Get-AvmCsvData -ModuleIndex $moduleIndex | Where-Object ModuleName -EQ $moduleName + + if (($module.ModuleStatus -ne 'Module Orphaned :eyes:') -and (-not ([string]::IsNullOrEmpty($module.PrimaryModuleOwnerGHHandle)))) { + $ProjectNumber = 566 # AVM - Module Issues + $comment = @" +> [!IMPORTANT] +> @$($module.ModuleOwnersGHTeam), the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. If you are not able to do so, please inform the AVM core team to take over. +"@ + # assign owner + $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo + + if ([String]::IsNullOrEmpty($assign)) { + if ($PSCmdlet.ShouldProcess("missing user comment to issue [$($issue.title)]", 'Add')) { + $comment = @" +> [!WARNING] +> This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support). +"@ + + gh issue comment $issue.url --body $reply --repo $Repo + } + } + } + } + + # add issue to project + Add-GithubIssueToProject -Repo $Repo -ProjectNumber $ProjectNumber -IssueUrl $issueUrl + # add comment + gh issue comment $issueUrl --body $comment --repo $Repo } $issuesCreated++ diff --git a/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 new file mode 100644 index 0000000000..cd9d0222e4 --- /dev/null +++ b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 @@ -0,0 +1,57 @@ +<# +.SYNOPSIS +Adds an existing GitHub issue to an existing GitHub project (the new type, not the classic ones) + +.DESCRIPTION +Adds an existing GitHub issue to an existing GitHub project (the new type, not the classic ones) + +.PARAMETER Repo +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER ProjectNumber +Mandatory. The GitHub project number (see last part of project URL, for example 538 for https://github.com/orgs/Azure/projects/538) + +.PARAMETER IssueUrl +Mandatory. The URL of the GitHub issue, like 'https://github.com/Azure/bicep-registry-modules/issues/757' + +.EXAMPLE +Add-GithubIssueToProject -Repo 'Azure/bicep-registry-modules' -ProjectNumber 538 -IssueUrl 'https://github.com/Azure/bicep-registry-modules/issues/757' + +.NOTES +Needs to run under a context with the permissions to read/write organization projects +#> +function Add-GithubIssueToProject { + param ( + [Parameter(Mandatory = $true)] + [string] $Repo, + + [Parameter(Mandatory = $true)] + [int] $ProjectNumber, + + [Parameter(Mandatory = $true)] + [string] $IssueUrl + ) + + $Organization = $Repo.Split('/')[0] + + $Project = gh api graphql -f query=' + query($organization: String! $number: Int!){ + organization(login: $organization){ + projectV2(number: $number) { + id + } + } + }' -f organization=$Organization -F number=$ProjectNumber | ConvertFrom-Json -Depth 10 + + $ProjectId = $Project.data.organization.projectV2.id + $IssueId = (gh issue view $IssueUrl --repo $Repo --json 'id' | ConvertFrom-Json -Depth 100).id + + gh api graphql -f query=' + mutation($project:ID!, $issue:ID!) { + addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) { + item { + id + } + } + }' -f project=$ProjectId -f issue=$IssueId +} diff --git a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 new file mode 100644 index 0000000000..821f476bb7 --- /dev/null +++ b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS +Parses AVM module CSV file + +.DESCRIPTION +Depending on the parameter, the correct CSV file will be parsed and returned a an object + +.PARAMETER ModuleIndex +Mandatory. Type of CSV file, that should be parsed ('Bicep-Resource', 'Bicep-Pattern') + +.EXAMPLE +Get-AvmCsvData -ModuleIndex 'Bicep-Resource' + +Parse the AVM Bicep modules +#> +Function Get-AvmCsvData { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateSet('Bicep-Resource', 'Bicep-Pattern')] + [string] $ModuleIndex + ) + + # CSV file URLs + $BicepResourceUrl = 'https://aka.ms/avm/index/bicep/res/csv' + $BicepPatternUrl = 'https://aka.ms/avm/index/bicep/ptn/csv' + + # Retrieve the CSV file + switch ($ModuleIndex) { + 'Bicep-Resource' { + try { + $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl + } catch { + throw 'Unable to retrieve CSV file - Check network connection.' + } + } + 'Bicep-Pattern' { + try { + $unfilteredCSV = Invoke-WebRequest -Uri $BicepPatternUrl + } catch { + throw 'Unable to retrieve CSV file - Check network connection.' + } + } + } + + # Convert the CSV content to a PowerShell object + $formattedBicepFullCsv = ConvertFrom-Csv $unfilteredCSV.Content + + # Loop through each item in the filtered data + foreach ($item in $formattedBicepFullCsv) { + # Remove '@Azure/' from the ModuleOwnersGHTeam property + $item.ModuleOwnersGHTeam = $item.ModuleOwnersGHTeam -replace '@Azure\/', '' + # Remove '@Azure/' from the ModuleContributorsGHTeam property + $item.ModuleContributorsGHTeam = $item.ModuleContributorsGHTeam -replace '@Azure\/', '' + } + + # Return the modified data + return $formattedBicepFullCsv +} From 78f654c7e0c7077295d78660f0c565aa660a5442 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 12 Apr 2024 18:24:05 +0200 Subject: [PATCH 27/66] feat: AADDS change to trigger module publish (#1677) ## Description Added AADDS change to trigger module publish ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.aad.domain-service](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml/badge.svg?branch=users%2Falsehr%2FaadUpdate&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml) | --- avm/res/aad/domain-service/README.md | 2 +- avm/res/aad/domain-service/main.bicep | 2 +- avm/res/aad/domain-service/main.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index 87f33b1d5f..fed0bb3998 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -1,6 +1,6 @@ # Azure Active Directory Domain Services `[Microsoft.AAD/domainServices]` -This module deploys an Azure Active Directory Domain Services (AADDS). +This module deploys an Azure Active Directory Domain Services (AADDS) instance. ## Navigation diff --git a/avm/res/aad/domain-service/main.bicep b/avm/res/aad/domain-service/main.bicep index cd0e56bfcd..ee8122ec51 100644 --- a/avm/res/aad/domain-service/main.bicep +++ b/avm/res/aad/domain-service/main.bicep @@ -1,5 +1,5 @@ metadata name = 'Azure Active Directory Domain Services' -metadata description = 'This module deploys an Azure Active Directory Domain Services (AADDS).' +metadata description = 'This module deploys an Azure Active Directory Domain Services (AADDS) instance.' metadata owner = 'Azure/module-maintainers' @minLength(1) diff --git a/avm/res/aad/domain-service/main.json b/avm/res/aad/domain-service/main.json index c210fd41ff..b82ba31545 100644 --- a/avm/res/aad/domain-service/main.json +++ b/avm/res/aad/domain-service/main.json @@ -6,10 +6,10 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "10683253750371964167" + "templateHash": "7265620724598107360" }, "name": "Azure Active Directory Domain Services", - "description": "This module deploys an Azure Active Directory Domain Services (AADDS).", + "description": "This module deploys an Azure Active Directory Domain Services (AADDS) instance.", "owner": "Azure/module-maintainers" }, "definitions": { From 1dea305bbba20e9c6173d6257649a0cbcd5e5403 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Fri, 12 Apr 2024 19:33:32 +0200 Subject: [PATCH 28/66] feat: Add additional logic if module issue is created (#1404) More fine grained logic, regarding commenting and assigning new module issues --------- Co-authored-by: Alexander Sehr Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../Set-AvmGitHubIssueOwnerConfig.ps1 | 133 +++++++----------- 1 file changed, 53 insertions(+), 80 deletions(-) diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 index 7cce7865d1..42bbfda099 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 @@ -1,63 +1,3 @@ -<# -.SYNOPSIS -Parses AVM module CSV file - -.DESCRIPTION -Depending on the parameter, the correct CSV file will be parsed and returned a an object - -.PARAMETER ModuleIndex -Type of CSV file, that should be parsed ('Bicep-Resource', 'Bicep-Pattern') - -.EXAMPLE -Next line will parse the AVM Bicep modules -Get-AvmCsvData -ModuleIndex 'Bicep-Resource' - -#> -Function Get-AvmCsvData { - [CmdletBinding()] - param ( - [Parameter(Mandatory)] - [ValidateSet('Bicep-Resource', 'Bicep-Pattern')] - [string] $ModuleIndex - ) - - # CSV file URLs - $BicepResourceUrl = 'https://aka.ms/avm/index/bicep/res/csv' - $BicepPatternUrl = 'https://aka.ms/avm/index/bicep/ptn/csv' - - # Retrieve the CSV file - switch ($ModuleIndex) { - 'Bicep-Resource' { - try { - $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl - } catch { - Write-Error 'Unable to retrieve CSV file - Check network connection.' - } - } - 'Bicep-Pattern' { - try { - $unfilteredCSV = Invoke-WebRequest -Uri $BicepPatternUrl - } catch { - Write-Error 'Unable to retrieve CSV file - Check network connection.' - } - } - } - - # Convert the CSV content to a PowerShell object - $formattedBicepFullCsv = ConvertFrom-Csv $unfilteredCSV.Content - - # Loop through each item in the filtered data - foreach ($item in $formattedBicepFullCsv) { - # Remove '@Azure/' from the ModuleOwnersGHTeam property - $item.ModuleOwnersGHTeam = $item.ModuleOwnersGHTeam -replace '@Azure/', '' - # Remove '@Azure/' from the ModuleContributorsGHTeam property - $item.ModuleContributorsGHTeam = $item.ModuleContributorsGHTeam -replace '@Azure/', '' - } - - # Return the modified data - return $formattedBicepFullCsv -} - <# .SYNOPSIS Assigns issues to module owners and adds comments and labels @@ -66,10 +6,13 @@ Assigns issues to module owners and adds comments and labels For the given issue, the module owner (according to the AVM CSV file) will be notified in a comment and assigned to the issue .PARAMETER Repo -Repository name according to GitHub (owner/name) +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER RepoRoot +Optional. Path to the root of the repository. .PARAMETER IssueUrl -The full GitHub URL to the issue +Mandatory. The URL of the GitHub issue, like 'https://github.com/Azure/bicep-registry-modules/issues/757' .EXAMPLE Set-AvmGitHubIssueOwnerConfig -Repo 'Azure/bicep-registry-modules' -IssueUrl 'https://github.com/Azure/bicep-registry-modules/issues/757' @@ -84,9 +27,15 @@ function Set-AvmGitHubIssueOwnerConfig { [string] $Repo, [Parameter(Mandatory = $true)] - [string] $IssueUrl + [string] $IssueUrl, + + [Parameter(Mandatory = $false)] + [string] $RepoRoot = (Get-Item -Path $PSScriptRoot).parent.parent.parent.parent.FullName ) + # Loading helper functions + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + $issue = gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --json 'author,title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 if ($issue.title.StartsWith('[AVM Module Issue]')) { @@ -97,23 +46,41 @@ function Set-AvmGitHubIssueOwnerConfig { } $moduleIndex = $moduleName.StartsWith('avm/res') ? 'Bicep-Resource' : 'Bicep-Pattern' + # get CSV data $module = Get-AvmCsvData -ModuleIndex $moduleIndex | Where-Object ModuleName -EQ $moduleName - if ([string]::IsNullOrEmpty($module)) { - throw "Module $moduleName was not found in $moduleIndex CSV file." - } + # new/unknown module + if ($null -eq $module) { + $reply = @" +**@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** - $reply = @" -@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module! +> [!IMPORTANT] +> The module does not exist yet, we look into it. Please file a new module proposal under [AVM Module proposal](https://aka.ms/avm/moduleproposal). +"@ + } + # orphaned module + elseif ($module.ModuleStatus -eq 'Module Orphaned :eyes:') { + $reply = @" +**@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** -A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleContributorsGHTeam) team will review it soon! +> [!IMPORTANT] +> Please note, that this module is currently orphaned. The @Azure/avm-core-team-technical-bicep, will attempt to find an owner for it. In the meantime, the core team may assist with this issue. Thank you for your patience! "@ + } + # existing module + else { + $reply = @" +**@$($issue.author.login), thanks for submitting this issue for the ``$moduleName`` module!** - if ($PSCmdlet.ShouldProcess("attention label to issue [$($issue.title)]", 'Add')) { - # add labels - gh issue edit $issue.url --add-label 'Needs: Attention :wave:' --repo $Repo +> [!IMPORTANT] +> A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleContributorsGHTeam) team will review it soon! +"@ } + # add issue to project + $ProjectNumber = 566 # AVM - Module Issues + Add-GithubIssueToProject -Repo $Repo -ProjectNumber $ProjectNumber -IssueUrl $IssueUrl + if ($PSCmdlet.ShouldProcess("class label to issue [$($issue.title)]", 'Add')) { gh issue edit $issue.url --add-label ($moduleIndex -eq 'Bicep-Resource' ? 'Class: Resource Module :package:' : 'Class: Pattern Module :package:') --repo $Repo } @@ -123,15 +90,21 @@ A member of the @azure/$($module.ModuleOwnersGHTeam) or @azure/$($module.ModuleC gh issue comment $issue.url --body $reply --repo $Repo } - if ($PSCmdlet.ShouldProcess(("owner [{0}] to issue [$($issue.title)]" -f $module.PrimaryModuleOwnerGHHandle), 'Assign')) { - # assign owner - $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo - } + if (($module.ModuleStatus -ne 'Module Orphaned :eyes:') -and (-not ([string]::IsNullOrEmpty($module.PrimaryModuleOwnerGHHandle)))) { + if ($PSCmdlet.ShouldProcess(("owner [{0}] to issue [$($issue.title)]" -f $module.PrimaryModuleOwnerGHHandle), 'Assign')) { + # assign owner + $assign = gh issue edit $issue.url --add-assignee $module.PrimaryModuleOwnerGHHandle --repo $Repo + } + + if ([String]::IsNullOrEmpty($assign)) { + if ($PSCmdlet.ShouldProcess("missing user comment to issue [$($issue.title)]", 'Add')) { + $reply = @" +> [!WARNING] +> This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support). +"@ - if ([String]::IsNullOrEmpty($assign)) { - if ($PSCmdlet.ShouldProcess("missing user comment to issue [$($issue.title)]", 'Add')) { - $reply = "This issue couldn't be assigend due to an internal error. @$($module.PrimaryModuleOwnerGHHandle), please make sure this issue is assigned to you and please provide an initial response as soon as possible, in accordance with the [AVM Support statement](https://aka.ms/AVM/Support)." - gh issue comment $issue.url --body $reply --repo $Repo + gh issue comment $issue.url --body $reply --repo $Repo + } } } } From 9ddcafcff7113e7c1328b232755e5646b8ce96df Mon Sep 17 00:00:00 2001 From: Fabio Masciotra Date: Fri, 12 Apr 2024 19:42:45 +0200 Subject: [PATCH 29/66] fix: module `avm/res/network/connection` (#1670) ## Description Updated with new requirements for PublicIP: Standard Sku and Static Allocation Method Fixes #1529 Fixes #1600 Fixes #1384 Closes #1529 Closes #1600 Closes #1384 --> ## Pipeline Reference [![avm.res.network.connection](https://github.com/fabmas/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml/badge.svg?branch=netconn)](https://github.com/fabmas/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml) | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../connection/tests/e2e/defaults/dependencies.bicep | 12 ++++++++++++ .../connection/tests/e2e/max/dependencies.bicep | 12 ++++++++++++ .../tests/e2e/waf-aligned/dependencies.bicep | 12 ++++++++++++ 3 files changed, 36 insertions(+) diff --git a/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep b/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep index a8398dc99e..3cb8518745 100644 --- a/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/network/connection/tests/e2e/defaults/dependencies.bicep @@ -42,6 +42,12 @@ resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: primaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { @@ -95,6 +101,12 @@ resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: secondaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { diff --git a/avm/res/network/connection/tests/e2e/max/dependencies.bicep b/avm/res/network/connection/tests/e2e/max/dependencies.bicep index a8398dc99e..f180cae86e 100644 --- a/avm/res/network/connection/tests/e2e/max/dependencies.bicep +++ b/avm/res/network/connection/tests/e2e/max/dependencies.bicep @@ -42,6 +42,12 @@ resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: primaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { @@ -95,6 +101,12 @@ resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: secondaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { diff --git a/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep index a8398dc99e..f180cae86e 100644 --- a/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/network/connection/tests/e2e/waf-aligned/dependencies.bicep @@ -42,6 +42,12 @@ resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: primaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { @@ -95,6 +101,12 @@ resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: secondaryPublicIPName location: location + properties: { + publicIPAllocationMethod: 'Static' + } + sku: { + name: 'Standard' + } } resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2023-04-01' = { From 07515bd8c093e9d35c6c8d439d0acff36a349de8 Mon Sep 17 00:00:00 2001 From: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:20:38 +0100 Subject: [PATCH 30/66] feat: Adding fix to allow multiple telemtry deployments (#1671) ## Description Feat: Adding fix to allow multiple telemetry deployments Tested by adding additional telemtry deploy to DigitalTwins module and runner pester tests locally. Pester test now searches for telemetry with a name staring with `46d3xbcp*` as the unique ID for AVM. Fixes #1612 ![image](https://github.com/Azure/bicep-registry-modules/assets/48600046/cf32ae21-cc0c-4455-b5f7-81f3ee5b7f80) ## Pipeline Reference | Pipeline | | -------- | | N/A | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../staticValidation/compliance/module.tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 index 72bbe86ff0..3aa360938f 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 @@ -833,7 +833,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' } # The AVM telemetry prefix $telemetryDeployment | Should -Not -BeNullOrEmpty -Because 'A telemetry resource with name prefix [46d3xbcp] should be present in the template' } @@ -850,7 +850,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' @@ -873,7 +873,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' @@ -927,7 +927,7 @@ Describe 'Module tests' -Tag 'Module' { } else { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' }).name # The AVM telemetry prefix + $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' }).name # The AVM telemetry prefix $telemetryDeploymentName | Should -Match "$expectedTelemetryIdentifier" } } From fadfdff7f1e7a2d877f668edaac8ecfe126ba3e8 Mon Sep 17 00:00:00 2001 From: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:39:17 +0100 Subject: [PATCH 31/66] Telemetry (#1678) ## Description Bugfix for latest Static Validation Change ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.analysis-services.server](https://github.com/ChrisSidebotham/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml/badge.svg?branch=telemetry)](https://github.com/ChrisSidebotham/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml) | ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../staticValidation/compliance/module.tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 index 3aa360938f..938ec4556d 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 @@ -833,7 +833,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix $telemetryDeployment | Should -Not -BeNullOrEmpty -Because 'A telemetry resource with name prefix [46d3xbcp] should be present in the template' } @@ -850,7 +850,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' @@ -873,7 +873,7 @@ Describe 'Module tests' -Tag 'Module' { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' } # The AVM telemetry prefix + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' @@ -927,7 +927,7 @@ Describe 'Module tests' -Tag 'Module' { } else { $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } } - $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '46d3xbcp*' }).name # The AVM telemetry prefix + $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' }).name # The AVM telemetry prefix $telemetryDeploymentName | Should -Match "$expectedTelemetryIdentifier" } } From 9a437094afc4590f5f185466cce1a6912f9316f4 Mon Sep 17 00:00:00 2001 From: hundredacres Date: Sat, 13 Apr 2024 01:32:02 -0700 Subject: [PATCH 32/66] fix: Fixed WAF alignment for default `avm/res/app/managed-environment` (#1391) ## Description Fixes WAF alignment for defaults. Closes https://github.com/Azure/bicep-registry-modules/issues/1186 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.app.managed-environment](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml/badge.svg?branch=waf_review)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml) | ## Type of Change - [X] Azure Verified Module updates: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- avm/res/app/managed-environment/README.md | 156 +++++++++++------- avm/res/app/managed-environment/main.bicep | 111 +++++++------ avm/res/app/managed-environment/main.json | 20 +-- .../tests/e2e/defaults/dependencies.bicep | 37 +++++ .../tests/e2e/defaults/main.test.bicep | 17 +- 5 files changed, 226 insertions(+), 115 deletions(-) diff --git a/avm/res/app/managed-environment/README.md b/avm/res/app/managed-environment/README.md index b91247a73e..feac29db4e 100644 --- a/avm/res/app/managed-environment/README.md +++ b/avm/res/app/managed-environment/README.md @@ -48,7 +48,21 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' logAnalyticsWorkspaceResourceId: '' name: 'amemin001' // Non-required parameters + dockerBridgeCidr: '172.16.0.1/28' + infrastructureResourceGroupName: '' + infrastructureSubnetId: '' + internal: true location: '' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + workloadProfiles: [ + { + maximumCount: 3 + minimumCount: 0 + name: 'CAW01' + workloadProfileType: 'D4' + } + ] } } ``` @@ -73,8 +87,36 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' "value": "amemin001" }, // Non-required parameters + "dockerBridgeCidr": { + "value": "172.16.0.1/28" + }, + "infrastructureResourceGroupName": { + "value": "" + }, + "infrastructureSubnetId": { + "value": "" + }, + "internal": { + "value": true + }, "location": { "value": "" + }, + "platformReservedCidr": { + "value": "172.17.17.0/24" + }, + "platformReservedDnsIP": { + "value": "172.17.17.17" + }, + "workloadProfiles": { + "value": [ + { + "maximumCount": 3, + "minimumCount": 0, + "name": "CAW01", + "workloadProfileType": "D4" + } + ] } } } @@ -397,7 +439,13 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' | Parameter | Type | Description | | :-- | :-- | :-- | -| [`infrastructureSubnetId`](#parameter-infrastructuresubnetid) | string | Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. | +| [`dockerBridgeCidr`](#parameter-dockerbridgecidr) | string | CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`infrastructureResourceGroupName`](#parameter-infrastructureresourcegroupname) | string | Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`infrastructureSubnetId`](#parameter-infrastructuresubnetid) | string | Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`internal`](#parameter-internal) | bool | Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`platformReservedCidr`](#parameter-platformreservedcidr) | string | IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`platformReservedDnsIP`](#parameter-platformreserveddnsip) | string | An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. | +| [`workloadProfiles`](#parameter-workloadprofiles) | array | Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant. | **Optional parameters** @@ -408,18 +456,12 @@ module managedEnvironment 'br/public:avm/res/app/managed-environment:' | [`daprAIConnectionString`](#parameter-dapraiconnectionstring) | securestring | Application Insights connection string used by Dapr to export Service to Service communication telemetry. | | [`daprAIInstrumentationKey`](#parameter-dapraiinstrumentationkey) | securestring | Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry. | | [`dnsSuffix`](#parameter-dnssuffix) | string | DNS suffix for the environment domain. | -| [`dockerBridgeCidr`](#parameter-dockerbridgecidr) | string | CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`infrastructureResourceGroupName`](#parameter-infrastructureresourcegroupname) | string | Name of the infrastructure resource group. If not provided, it will be set with a default value. | -| [`internal`](#parameter-internal) | bool | Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. | | [`location`](#parameter-location) | string | Location for all Resources. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`logsDestination`](#parameter-logsdestination) | string | Logs destination. | -| [`platformReservedCidr`](#parameter-platformreservedcidr) | string | IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. | -| [`platformReservedDnsIP`](#parameter-platformreserveddnsip) | string | An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-tags) | object | Tags of the resource. | -| [`workloadProfiles`](#parameter-workloadprofiles) | array | Workload profiles configured for the Managed Environment. | | [`zoneRedundant`](#parameter-zoneredundant) | bool | Whether or not this Managed Environment is zone-redundant. | ### Parameter: `logAnalyticsWorkspaceResourceId` @@ -436,14 +478,62 @@ Name of the Container Apps Managed Environment. - Required: Yes - Type: string +### Parameter: `dockerBridgeCidr` + +CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `infrastructureResourceGroupName` + +Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `[take(format('ME_{0}', parameters('name')), 63)]` + ### Parameter: `infrastructureSubnetId` -Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. +Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `internal` + +Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `platformReservedCidr` + +IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `platformReservedDnsIP` + +An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant. - Required: No - Type: string - Default: `''` +### Parameter: `workloadProfiles` + +Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant. + +- Required: No +- Type: array +- Default: `[]` + ### Parameter: `certificatePassword` Password of the certificate used by the custom domain. @@ -484,14 +574,6 @@ DNS suffix for the environment domain. - Type: string - Default: `''` -### Parameter: `dockerBridgeCidr` - -CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. - -- Required: No -- Type: string -- Default: `''` - ### Parameter: `enableTelemetry` Enable/Disable usage telemetry for module. @@ -500,22 +582,6 @@ Enable/Disable usage telemetry for module. - Type: bool - Default: `True` -### Parameter: `infrastructureResourceGroupName` - -Name of the infrastructure resource group. If not provided, it will be set with a default value. - -- Required: No -- Type: string -- Default: `[take(format('ME_{0}', parameters('name')), 63)]` - -### Parameter: `internal` - -Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. - -- Required: No -- Type: bool -- Default: `False` - ### Parameter: `location` Location for all Resources. @@ -568,22 +634,6 @@ Logs destination. - Type: string - Default: `'log-analytics'` -### Parameter: `platformReservedCidr` - -IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. - -- Required: No -- Type: string -- Default: `''` - -### Parameter: `platformReservedDnsIP` - -An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. - -- Required: No -- Type: string -- Default: `''` - ### Parameter: `roleAssignments` Array of role assignments to create. @@ -680,21 +730,13 @@ Tags of the resource. - Required: No - Type: object -### Parameter: `workloadProfiles` - -Workload profiles configured for the Managed Environment. - -- Required: No -- Type: array -- Default: `[]` - ### Parameter: `zoneRedundant` Whether or not this Managed Environment is zone-redundant. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/app/managed-environment/main.bicep b/avm/res/app/managed-environment/main.bicep index 9810f65087..6ed8dc58fd 100644 --- a/avm/res/app/managed-environment/main.bicep +++ b/avm/res/app/managed-environment/main.bicep @@ -31,23 +31,23 @@ param daprAIConnectionString string = '' @secure() param daprAIInstrumentationKey string = '' -@description('Optional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform.') +@description('Conditional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant.') param dockerBridgeCidr string = '' -@description('Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true.') +@description('Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if "internal" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant.') param infrastructureSubnetId string = '' -@description('Optional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided.') +@description('Conditional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then "infrastructureSubnetId" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant.') param internal bool = false -@description('Optional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform.') +@description('Conditional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant.') param platformReservedCidr string = '' -@description('Optional. An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform.') +@description('Conditional. An IP address from the IP range defined by "platformReservedCidr" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant.') param platformReservedDnsIP string = '' @description('Optional. Whether or not this Managed Environment is zone-redundant.') -param zoneRedundant bool = false +param zoneRedundant bool = true @description('Optional. Password of the certificate used by the custom domain.') @secure() @@ -63,42 +63,50 @@ param dnsSuffix string = '' @description('Optional. The lock settings of the service.') param lock lockType -@description('Optional. Workload profiles configured for the Managed Environment.') +@description('Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant.') param workloadProfiles array = [] -@description('Optional. Name of the infrastructure resource group. If not provided, it will be set with a default value.') +@description('Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant.') param infrastructureResourceGroupName string = take('ME_${name}', 63) var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') - 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) } -resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { - name: '46d3xbcp.res.app-managedenvironment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' - properties: { - mode: 'Incremental' - template: { - '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' - contentVersion: '1.0.0.0' - resources: [] - outputs: { - telemetry: { - type: 'String' - value: 'For more information, see https://aka.ms/avm/TelemetryInfo' +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.app-managedenvironment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } } } } } -} -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = if (!empty(logAnalyticsWorkspaceResourceId)) { - name: last(split(logAnalyticsWorkspaceResourceId, '/'))! - scope: resourceGroup(split(logAnalyticsWorkspaceResourceId, '/')[2], split(logAnalyticsWorkspaceResourceId, '/')[4]) -} +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = + if (!empty(logAnalyticsWorkspaceResourceId)) { + name: last(split(logAnalyticsWorkspaceResourceId, '/'))! + scope: resourceGroup(split(logAnalyticsWorkspaceResourceId, '/')[2], split(logAnalyticsWorkspaceResourceId, '/')[4]) + } resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { name: name @@ -132,28 +140,37 @@ resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { } } -resource managedEnvironment_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (roleAssignment, index) in (roleAssignments ?? []): { - name: guid(managedEnvironment.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) - properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') ? roleAssignment.roleDefinitionIdOrName : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) - principalId: roleAssignment.principalId - description: roleAssignment.?description - principalType: roleAssignment.?principalType - condition: roleAssignment.?condition - conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set - delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId +resource managedEnvironment_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(managedEnvironment.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: managedEnvironment } - scope: managedEnvironment -}] - -resource managedEnvironment_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { - name: lock.?name ?? 'lock-${name}' - properties: { - level: lock.?kind ?? '' - notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' +] + +resource managedEnvironment_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: managedEnvironment } - scope: managedEnvironment -} @description('The name of the resource group the Managed Environment was deployed into.') output resourceGroupName string = resourceGroup().name diff --git a/avm/res/app/managed-environment/main.json b/avm/res/app/managed-environment/main.json index 5fd1fd0e1a..005a9294d1 100644 --- a/avm/res/app/managed-environment/main.json +++ b/avm/res/app/managed-environment/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "12287523603498112228" + "version": "0.26.54.24096", + "templateHash": "1208664329573960589" }, "name": "App ManagedEnvironments", "description": "This module deploys an App Managed Environment (also known as a Container App Environment).", @@ -170,40 +170,40 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform." + "description": "Conditional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "infrastructureSubnetId": { "type": "string", "defaultValue": "", "metadata": { - "description": "Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if \"internal\" is set to true." + "description": "Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if \"internal\" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "internal": { "type": "bool", "defaultValue": false, "metadata": { - "description": "Optional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then \"infrastructureSubnetId\" must be provided." + "description": "Conditional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then \"infrastructureSubnetId\" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "platformReservedCidr": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform." + "description": "Conditional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "platformReservedDnsIP": { "type": "string", "defaultValue": "", "metadata": { - "description": "Optional. An IP address from the IP range defined by \"platformReservedCidr\" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform." + "description": "Conditional. An IP address from the IP range defined by \"platformReservedCidr\" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { "description": "Optional. Whether or not this Managed Environment is zone-redundant." } @@ -239,14 +239,14 @@ "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. Workload profiles configured for the Managed Environment." + "description": "Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant." } }, "infrastructureResourceGroupName": { "type": "string", "defaultValue": "[take(format('ME_{0}', parameters('name')), 63)]", "metadata": { - "description": "Optional. Name of the infrastructure resource group. If not provided, it will be set with a default value." + "description": "Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant." } } }, diff --git a/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep b/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep index 737827c1fd..c4ea8327c4 100644 --- a/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/app/managed-environment/tests/e2e/defaults/dependencies.bicep @@ -4,6 +4,11 @@ param location string = resourceGroup().location @description('Required. The name of the Log Analytics Workspace to create.') param logAnalyticsWorkspaceName string +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { name: logAnalyticsWorkspaceName location: location @@ -18,5 +23,37 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10 }) } +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + delegations: [ + { + name: 'Microsoft.App.environments' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + } + ] + } + +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + @description('The resource ID of the created Log Analytics Workspace.') output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep b/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep index 53f5ae658f..2e2152ec1a 100644 --- a/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep +++ b/avm/res/app/managed-environment/tests/e2e/defaults/main.test.bicep @@ -35,6 +35,7 @@ module nestedDependencies 'dependencies.bicep' = { name: '${uniqueString(deployment().name, resourceLocation)}-paramNested' params: { location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' } } @@ -49,8 +50,22 @@ module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation logAnalyticsWorkspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + location: resourceLocation + workloadProfiles: [ + { + workloadProfileType: 'D4' + name: 'CAW01' + minimumCount: 0 + maximumCount: 3 + } + ] + internal: true + dockerBridgeCidr: '172.16.0.1/28' + platformReservedCidr: '172.17.17.0/24' + platformReservedDnsIP: '172.17.17.17' + infrastructureSubnetId: nestedDependencies.outputs.subnetResourceId + infrastructureResourceGroupName: 'me-${resourceGroupName}' } dependsOn: [ nestedDependencies From c4e02e28857aad818f4d42d5c305b38afc51e539 Mon Sep 17 00:00:00 2001 From: hundredacres Date: Sun, 14 Apr 2024 00:06:05 -0700 Subject: [PATCH 33/66] fix: Update for default WAF-alignment - `avm/res/cache/redis` (#1621) ## Description Updating default to be WAF-aligned. Fixes #1504 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.cache.redis](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=fix%2Fredis%2Fcache%2F1504)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [X] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/cache/redis/README.md | 31 ++++++++++++++----- avm/res/cache/redis/main.bicep | 10 +++--- avm/res/cache/redis/main.json | 19 ++++++++---- .../tests/e2e/waf-aligned/main.test.bicep | 4 ++- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index a8e005dfd5..35061323b5 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -398,6 +398,8 @@ module redis 'br/public:avm/res/cache/redis:' = { } ] redisVersion: '6' + replicasPerMaster: 3 + replicasPerPrimary: 3 shardCount: 1 skuName: 'Premium' tags: { @@ -408,6 +410,7 @@ module redis 'br/public:avm/res/cache/redis:' = { zones: [ 1 2 + 3 ] } } @@ -487,6 +490,12 @@ module redis 'br/public:avm/res/cache/redis:' = { "redisVersion": { "value": "6" }, + "replicasPerMaster": { + "value": 3 + }, + "replicasPerPrimary": { + "value": 3 + }, "shardCount": { "value": 1 }, @@ -505,7 +514,8 @@ module redis 'br/public:avm/res/cache/redis:' = { "zones": { "value": [ 1, - 2 + 2, + 3 ] } } @@ -541,7 +551,7 @@ module redis 'br/public:avm/res/cache/redis:' = { | [`redisConfiguration`](#parameter-redisconfiguration) | object | All Redis Settings. Few possible keys: rdb-backup-enabled,rdb-storage-connection-string,rdb-backup-frequency,maxmemory-delta,maxmemory-policy,notify-keyspace-events,maxmemory-samples,slowlog-log-slower-than,slowlog-max-len,list-max-ziplist-entries,list-max-ziplist-value,hash-max-ziplist-entries,hash-max-ziplist-value,set-max-intset-entries,zset-max-ziplist-entries,zset-max-ziplist-value etc. | | [`redisVersion`](#parameter-redisversion) | string | Redis version. Only major version will be used in PUT/PATCH request with current valid values: (4, 6). | | [`replicasPerMaster`](#parameter-replicaspermaster) | int | The number of replicas to be created per primary. | -| [`replicasPerPrimary`](#parameter-replicasperprimary) | int | The number of replicas to be created per primary. | +| [`replicasPerPrimary`](#parameter-replicasperprimary) | int | The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`shardCount`](#parameter-shardcount) | int | The number of shards to be created on a Premium Cluster Cache. | | [`skuName`](#parameter-skuname) | string | The type of Redis cache to deploy. | @@ -1217,15 +1227,15 @@ The number of replicas to be created per primary. - Required: No - Type: int -- Default: `1` +- Default: `3` ### Parameter: `replicasPerPrimary` -The number of replicas to be created per primary. +The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache. - Required: No - Type: int -- Default: `1` +- Default: `3` ### Parameter: `roleAssignments` @@ -1330,7 +1340,7 @@ The type of Redis cache to deploy. - Required: No - Type: string -- Default: `'Basic'` +- Default: `'Premium'` - Allowed: ```Bicep [ @@ -1385,7 +1395,14 @@ If the zoneRedundant parameter is true, replicas will be provisioned in the avai - Required: No - Type: array -- Default: `[]` +- Default: + ```Bicep + [ + 1 + 2 + 3 + ] + ``` ## Outputs diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index a7975ce724..7cd77eda1c 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -51,11 +51,11 @@ param redisVersion string = '6' @minValue(1) @description('Optional. The number of replicas to be created per primary.') -param replicasPerMaster int = 1 +param replicasPerMaster int = 3 @minValue(1) -@description('Optional. The number of replicas to be created per primary.') -param replicasPerPrimary int = 1 +@description('Optional. The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache.') +param replicasPerPrimary int = 3 @minValue(1) @description('Optional. The number of shards to be created on a Premium Cluster Cache.') @@ -79,7 +79,7 @@ param capacity int = 1 'Standard' ]) @description('Optional. The type of Redis cache to deploy.') -param skuName string = 'Basic' +param skuName string = 'Premium' @description('Optional. Static IP address. Optionally, may be specified when deploying a Redis cache inside an existing Azure Virtual Network; auto assigned by default.') param staticIP string = '' @@ -94,7 +94,7 @@ param tenantSettings object = {} param zoneRedundant bool = true @description('Optional. If the zoneRedundant parameter is true, replicas will be provisioned in the availability zones specified here. Otherwise, the service will choose where replicas are deployed.') -param zones array = [] +param zones int[] = [1, 2, 3] @description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') param privateEndpoints privateEndpointType diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index 0dcf9d3c88..b7da02c047 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "7874819819895628744" + "templateHash": "15170120544539480286" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache.", @@ -535,7 +535,7 @@ }, "replicasPerMaster": { "type": "int", - "defaultValue": 1, + "defaultValue": 3, "minValue": 1, "metadata": { "description": "Optional. The number of replicas to be created per primary." @@ -543,10 +543,10 @@ }, "replicasPerPrimary": { "type": "int", - "defaultValue": 1, + "defaultValue": 3, "minValue": 1, "metadata": { - "description": "Optional. The number of replicas to be created per primary." + "description": "Optional. The number of replicas to be created per primary. Needs to be the same as replicasPerMaster for a Premium Cluster Cache." } }, "shardCount": { @@ -575,7 +575,7 @@ }, "skuName": { "type": "string", - "defaultValue": "Basic", + "defaultValue": "Premium", "allowedValues": [ "Basic", "Premium", @@ -615,7 +615,14 @@ }, "zones": { "type": "array", - "defaultValue": [], + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], "metadata": { "description": "Optional. If the zoneRedundant parameter is true, replicas will be provisioned in the availability zones specified here. Otherwise, the service will choose where replicas are deployed." } diff --git a/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep b/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep index bd46cbeaa6..6e86ccb362 100644 --- a/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/cache/redis/tests/e2e/waf-aligned/main.test.bicep @@ -88,7 +88,7 @@ module testDeployment '../../../main.bicep' = [ } minimumTlsVersion: '1.2' zoneRedundant: true - zones: [1, 2] + zones: [1, 2, 3] privateEndpoints: [ { privateDnsZoneResourceIds: [ @@ -103,6 +103,8 @@ module testDeployment '../../../main.bicep' = [ } ] redisVersion: '6' + replicasPerMaster: 3 + replicasPerPrimary: 3 shardCount: 1 skuName: 'Premium' managedIdentities: { From 4c0593947098a648c0c001a0aca39ea21f79a004 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Sun, 14 Apr 2024 09:28:52 +0200 Subject: [PATCH 34/66] fix: Aligned PurgeProtection (where required) across deployed test KeyVaults (#1675) ## Description Aligned PurgeProtection (where required) across deployed test KeyVaults - Set to 7 days (minimum for purge protection) - Added / adjusted comments to justify purge protection ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../tests/e2e/encr/dependencies.bicep | 4 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../dependencies.bicep | 2 +- .../dependencies.bicep | 2 +- .../e2e/accessPolicies/dependencies.bicep | 2 +- .../tests/e2e/defaults/dependencies.bicep | 2 +- .../tests/e2e/max/dependencies.bicep | 2 +- .../tests/e2e/waf-aligned/dependencies.bicep | 2 +- .../image/tests/e2e/max/dependencies.bicep | 2 +- .../tests/e2e/waf-aligned/dependencies.bicep | 2 +- .../tests/e2e/linux.ssecmk/dependencies.bicep | 2 +- .../e2e/windows.ssecmk/dependencies.bicep | 2 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../tests/e2e/azure/dependencies.bicep | 2 +- .../tests/e2e/max/dependencies.bicep | 4 +- .../tests/e2e/waf-aligned/dependencies.bicep | 4 +- .../tests/e2e/max/dependencies2.bicep | 8 +-- .../tests/e2e/encr/dependencies.bicep | 63 ++++++++++--------- .../lab/tests/e2e/max/dependencies.bicep | 2 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../tests/e2e/encr/dependencies.bicep | 2 +- .../dependencies.bicep | 7 ++- .../dependencies.bicep | 7 ++- .../tests/e2e/encrwsai/dependencies.bicep | 2 +- .../tests/e2e/encrwuai/dependencies.bicep | 2 +- 28 files changed, 74 insertions(+), 65 deletions(-) diff --git a/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep b/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep index bd17946f56..8e5006c291 100644 --- a/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep +++ b/avm/res/app-configuration/configuration-store/tests/e2e/encr/dependencies.bicep @@ -21,8 +21,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 90 + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true diff --git a/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep b/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep index 49d0dfa3aa..859e78741b 100644 --- a/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep +++ b/avm/res/automation/automation-account/tests/e2e/encr/dependencies.bicep @@ -21,8 +21,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 - enablePurgeProtection: true enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true diff --git a/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep b/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep index 7519eccd2b..7d610a83e7 100644 --- a/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep +++ b/avm/res/batch/batch-account/tests/e2e/encr/dependencies.bicep @@ -74,7 +74,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep index 64f4ad94b8..a9da625760 100644 --- a/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/cognitive-services/account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep @@ -31,7 +31,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep index 1ad042356a..b12458a9b8 100644 --- a/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/cognitive-services/account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep index 4bc293926f..810b6e459f 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/accessPolicies/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep index a79f18bab3..19eadf3cfc 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/defaults/dependencies.bicep @@ -13,7 +13,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep index e6e79856d7..bf2bbaace8 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/max/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep index 336e29b226..eb651cd09b 100644 --- a/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/disk-encryption-set/tests/e2e/waf-aligned/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/image/tests/e2e/max/dependencies.bicep b/avm/res/compute/image/tests/e2e/max/dependencies.bicep index 6a26edc6cf..229b7d2262 100644 --- a/avm/res/compute/image/tests/e2e/max/dependencies.bicep +++ b/avm/res/compute/image/tests/e2e/max/dependencies.bicep @@ -155,7 +155,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for encrption to work + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep index 6a26edc6cf..229b7d2262 100644 --- a/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/image/tests/e2e/waf-aligned/dependencies.bicep @@ -155,7 +155,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for encrption to work + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep b/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep index 324e66e29f..4d041e8ada 100644 --- a/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep +++ b/avm/res/compute/virtual-machine-scale-set/tests/e2e/linux.ssecmk/dependencies.bicep @@ -50,7 +50,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep index 3ae96a4216..bb849994dd 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/dependencies.bicep @@ -41,7 +41,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by disk encryption set + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep b/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep index b908867ee2..1a2b461308 100644 --- a/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep +++ b/avm/res/container-instance/container-group/tests/e2e/encr/dependencies.bicep @@ -23,7 +23,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep b/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep index 3d3446c847..ba949b79ea 100644 --- a/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep +++ b/avm/res/container-registry/registry/tests/e2e/encr/dependencies.bicep @@ -46,7 +46,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep b/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep index e2274a7fbb..86b3297af0 100644 --- a/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep +++ b/avm/res/container-service/managed-cluster/tests/e2e/azure/dependencies.bicep @@ -67,7 +67,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-11-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by nodepool vmss + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep b/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep index 5bf7118978..ea6c34b09e 100644 --- a/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep +++ b/avm/res/databricks/workspace/tests/e2e/max/dependencies.bicep @@ -47,7 +47,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -73,7 +73,7 @@ resource keyVaultDisk 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep index f8aa5aab74..689a482cfd 100644 --- a/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/databricks/workspace/tests/e2e/waf-aligned/dependencies.bicep @@ -47,7 +47,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -73,7 +73,7 @@ resource keyVaultDisk 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by batch account + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep index 0077f627ce..2966ac2358 100644 --- a/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep +++ b/avm/res/db-for-my-sql/flexible-server/tests/e2e/max/dependencies2.bicep @@ -30,8 +30,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 90 + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true @@ -74,8 +74,8 @@ resource geoBackupKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 90 + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true enabledForDeployment: true diff --git a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep index 52be6351dc..367f400a42 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/tests/e2e/encr/dependencies.bicep @@ -8,44 +8,47 @@ param keyVaultName string param managedIdentityName string resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { - name: managedIdentityName - location: location + name: managedIdentityName + location: location } resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = { - name: keyVaultName - location: location - properties: { - sku: { - family: 'A' - name: 'standard' - } - tenantId: tenant().tenantId - enablePurgeProtection: true - softDeleteRetentionInDays: 7 - enabledForTemplateDeployment: true - enabledForDiskEncryption: true - enabledForDeployment: true - enableRbacAuthorization: true - accessPolicies: [] + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' } - - resource key 'keys@2023-07-01' = { - name: 'keyEncryptionKey' - properties: { - kty: 'RSA' - } + tenantId: tenant().tenantId + enablePurgeProtection: true // Required for encryption to work + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2023-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' } + } } resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') - scope: keyVault::key - properties: { - principalId: managedIdentity.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User - principalType: 'ServicePrincipal' - } + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User + principalType: 'ServicePrincipal' + } } @description('The resource ID of the created Managed Identity.') diff --git a/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep b/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep index 376758c41c..5e1c0b1d25 100644 --- a/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep +++ b/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep @@ -32,7 +32,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for encrption to work + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep b/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep index 6c4085b1e5..c8d5689b90 100644 --- a/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep +++ b/avm/res/event-hub/namespace/tests/e2e/encr/dependencies.bicep @@ -46,7 +46,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by event hub namespace + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep index 4e41990054..99e9ed9814 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/encr/dependencies.bicep @@ -24,7 +24,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required for CMK + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep b/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep index 5afdae26cd..c8d5689b90 100644 --- a/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/encr/dependencies.bicep @@ -46,7 +46,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true // Required by service bus namespace + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep b/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep index 287edca40f..26d25098d8 100644 --- a/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/storage/storage-account/tests/e2e/system-assigned-cmk-encryption/dependencies.bicep @@ -38,7 +38,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -60,7 +60,10 @@ resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: keyVault::key properties: { principalId: storageAccount.identity.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User principalType: 'ServicePrincipal' } } diff --git a/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep index f01760e1ff..a2f946fa9c 100644 --- a/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep +++ b/avm/res/storage/storage-account/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -21,7 +21,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true @@ -89,7 +89,10 @@ resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { scope: keyVault::key properties: { principalId: managedIdentity.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12338af0-0e69-4776-bea7-57ae8d297424') // Key Vault Crypto User + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User principalType: 'ServicePrincipal' } } diff --git a/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep b/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep index 6d7a736f40..d74453b7c8 100644 --- a/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep +++ b/avm/res/synapse/workspace/tests/e2e/encrwsai/dependencies.bicep @@ -16,7 +16,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true diff --git a/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep b/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep index 04f775602e..487efa3cbe 100644 --- a/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep +++ b/avm/res/synapse/workspace/tests/e2e/encrwuai/dependencies.bicep @@ -24,7 +24,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: 'standard' } tenantId: tenant().tenantId - enablePurgeProtection: true + enablePurgeProtection: true // Required for encryption to work softDeleteRetentionInDays: 7 enabledForTemplateDeployment: true enabledForDiskEncryption: true From 18604b4b6d03de203d23b7c1df834fbeae585c15 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:13:14 +0200 Subject: [PATCH 35/66] feat: if module list is not in sync with CSV file an issue is created (#1401) This code includes a pipeline, that runs daily and checks, if the module list drop down (for module issues) is up-to-date. If not, an issue is created. --- .../avm.platform.sync-avm-modules-list.yml | 36 +++++++++ .../platform/Sync-AvmModulesList.ps1 | 78 +++++++++++++++++++ .../platform/helper/Get-AvmCsvData.ps1 | 1 + 3 files changed, 115 insertions(+) create mode 100644 .github/workflows/avm.platform.sync-avm-modules-list.yml create mode 100644 avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 diff --git a/.github/workflows/avm.platform.sync-avm-modules-list.yml b/.github/workflows/avm.platform.sync-avm-modules-list.yml new file mode 100644 index 0000000000..7557309692 --- /dev/null +++ b/.github/workflows/avm.platform.sync-avm-modules-list.yml @@ -0,0 +1,36 @@ +name: .Platform - Sync AVM module list + +on: + schedule: + - cron: "30 4 * * *" # Every day at 4:30 am + workflow_dispatch: + +jobs: + sync-avm-modules-list: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: tibdex/github-app-token@v2 + id: generate-token + with: + app_id: ${{ secrets.TEAM_LINTER_APP_ID }} + private_key: ${{ secrets.TEAM_LINTER_PRIVATE_KEY }} + - name: sync avm modules list + shell: pwsh + env: + GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Sync-AvmModulesList.ps1') + + $functionInput = @{ + Repo = "${{ github.repository_owner }}/${{ github.event.repository.name }}" + RepoRoot = $env:GITHUB_WORKSPACE + } + + Sync-AvmModulesList @functionInput -Verbose diff --git a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 new file mode 100644 index 0000000000..7cdfa21e8d --- /dev/null +++ b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 @@ -0,0 +1,78 @@ +<# +.SYNOPSIS +If module list is not in sync with CSV file, an issue is created + +.DESCRIPTION +CSV data for moules and pattern is loaded and compared with the list in the issue template. If they are not in sync, an issue with the necessary changes is created + +.PARAMETER Repo +Mandatory. The name of the respository to scan. Needs to have the structure "/", like 'Azure/bicep-registry-modules/' + +.PARAMETER RepoRoot +Optional. Path to the root of the repository. + +.EXAMPLE +Sync-AvmModulesList -Repo 'Azure/bicep-registry-modules' + +.NOTES +Will be triggered by the workflow avm.platform.sync-avm-modules-list.yml +#> +function Sync-AvmModulesList { + param ( + [Parameter(Mandatory = $true)] + [string] $Repo, + + [Parameter(Mandatory = $false)] + [string] $RepoRoot = (Get-Item -Path $PSScriptRoot).parent.parent.parent.parent.FullName + ) + + # Loading helper functions + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Add-GithubIssueToProject.ps1') + + $workflowFilePath = Join-Path $RepoRoot '.github' 'ISSUE_TEMPLATE' 'avm_module_issue.yml' + + # get CSV data + $modules = Get-AvmCsvData -ModuleIndex 'Bicep-Resource' | Select-Object -Property 'ModuleName' + $patterns = Get-AvmCsvData -ModuleIndex 'Bicep-Pattern' | Select-Object -Property 'ModuleName' + + # build new strings + $prefix = ' - "' + $postfix = '"' + $newModuleLines = $modules | ForEach-Object { $prefix + $_.ModuleName + $postfix } + $newPatternLines = $patterns | ForEach-Object { $prefix + $_.ModuleName + $postfix } + + # parse workflow file + $workflowFileLines = Get-Content $workflowFilePath + $startIndex = 0 + $endIndex = 0 + + for ($lineNumber = 0; $lineNumber -lt $workflowFileLines.Count; $lineNumber++) { + if ($startIndex -gt 0 -and (-not ($workflowFileLines[$lineNumber]).Trim().StartsWith('- "avm/'))) { + $endIndex = $lineNumber + break + } + + if (($workflowFileLines[$lineNumber]).Trim() -eq '- "Other, as defined below..."') { + $startIndex = $lineNumber + } + } + + $oldLines = $workflowFileLines[($startIndex + 1)..($endIndex - 1)] + $newLines = $newModuleLines + $newPatternLines + $body = $newLines -join ([Environment]::NewLine) + + if ($oldLines -ne $newLines) { + $title = '[AVM core] Module(s) missing from AVM Module Issue template' + $label = 'Type: AVM :a: :v: :m:,Type: Hygiene :broom:,Needs: Triage :mag:' + $issues = gh issue list --state open --limit 500 --label $label --json 'title' --repo $Repo | ConvertFrom-Json -Depth 100 + + if ($issues.title -notcontains $title) { + # create issue + $issueUrl = gh issue create --title $title --body $body --label $label --repo $Repo + # add issue to project + $ProjectNumber = 538 # AVM - Issue Triage + Add-GithubIssueToProject -Repo $Repo -ProjectNumber $ProjectNumber -IssueUrl $issueUrl + } + } +} diff --git a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 index 821f476bb7..17f6e077b9 100644 --- a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 +++ b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 @@ -30,6 +30,7 @@ Function Get-AvmCsvData { 'Bicep-Resource' { try { $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl + } catch { throw 'Unable to retrieve CSV file - Check network connection.' } From 33db14045f75747b208f626e1735ba3adf7689f1 Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:13:28 +0200 Subject: [PATCH 36/66] feat: New Module `avm/res/load-test-service/load-test` (#1613) ## Description New module : Load-test-service.load-test ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.load-test-service.load-test](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml/badge.svg?branch=avm-load-test-service)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Alexander Sehr --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.load-test-service.load-test.yml | 86 +++ avm/res/load-test-service/load-test/README.md | 597 ++++++++++++++++++ .../load-test-service/load-test/main.bicep | 242 +++++++ avm/res/load-test-service/load-test/main.json | 378 +++++++++++ .../tests/e2e/defaults/main.test.bicep | 45 ++ .../tests/e2e/max/dependencies.bicep | 16 + .../load-test/tests/e2e/max/main.test.bicep | 73 +++ .../dependencies.bicep | 64 ++ .../main.test.bicep | 69 ++ .../tests/e2e/waf-aligned/main.test.bicep | 55 ++ .../load-test-service/load-test/version.json | 7 + 13 files changed, 1634 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.load-test-service.load-test.yml create mode 100644 avm/res/load-test-service/load-test/README.md create mode 100644 avm/res/load-test-service/load-test/main.bicep create mode 100644 avm/res/load-test-service/load-test/main.json create mode 100644 avm/res/load-test-service/load-test/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/load-test-service/load-test/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep create mode 100644 avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep create mode 100644 avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/main.test.bicep create mode 100644 avm/res/load-test-service/load-test/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/load-test-service/load-test/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dd7d4654fc..bf9aa06b23 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -68,7 +68,7 @@ /avm/res/key-vault/vault/ @Azure/avm-res-keyvault-vault-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/kubernetes-configuration/extension/ @Azure/avm-res-kubernetesconfiguration-extension-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/kubernetes-configuration/flux-configuration/ @Azure/avm-res-kubernetesconfiguration-fluxconfiguration-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/load-test-service/load-test/ @Azure/avm-res-loadtestservice-loadtest-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/load-test-service/load-test/ @Azure/avm-res-loadtestservice-loadtest-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/logic/workflow/ @Azure/avm-res-logic-workflow-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/machine-learning-services/workspace/ @Azure/avm-res-machinelearningservices-workspace-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/maintenance/maintenance-configuration/ @Azure/avm-res-maintenance-maintenanceconfiguration-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 9fea2a0a2e..77d58e66d1 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -101,7 +101,7 @@ body: - "avm/res/key-vault/vault" - "avm/res/kubernetes-configuration/extension" - "avm/res/kubernetes-configuration/flux-configuration" - # - "avm/res/load-test-service/load-test" + - "avm/res/load-test-service/load-test" - "avm/res/logic/workflow" - "avm/res/machine-learning-services/workspace" - "avm/res/maintenance/maintenance-configuration" diff --git a/.github/workflows/avm.res.load-test-service.load-test.yml b/.github/workflows/avm.res.load-test-service.load-test.yml new file mode 100644 index 0000000000..255a2abdca --- /dev/null +++ b/.github/workflows/avm.res.load-test-service.load-test.yml @@ -0,0 +1,86 @@ +name: "avm.res.load-test-service.load-test" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.load-test-service.load-test.yml" + - "avm/res/load-test-service/load-test/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/load-test-service/load-test" + workflowPath: ".github/workflows/avm.res.load-test-service.load-test.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/load-test-service/load-test/README.md b/avm/res/load-test-service/load-test/README.md new file mode 100644 index 0000000000..54ee6da52b --- /dev/null +++ b/avm/res/load-test-service/load-test/README.md @@ -0,0 +1,597 @@ +# Load Testing Service `[Microsoft.LoadTestService/loadTests]` + +This module deploys a Load test. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.LoadTestService/loadTests` | [2022-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.LoadTestService/2022-12-01/loadTests) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/load-test-service/load-test:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [Using Customer-Managed-Keys with User-Assigned identity](#example-3-using-customer-managed-keys-with-user-assigned-identity) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module loadTest 'br/public:avm/res/load-test-service/load-test:' = { + name: 'loadTestDeployment' + params: { + // Required parameters + name: 'ltmin001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "ltmin001" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module loadTest 'br/public:avm/res/load-test-service/load-test:' = { + name: 'loadTestDeployment' + params: { + // Required parameters + name: 'ltmax001' + // Non-required parameters + loadTestDescription: 'This is a test load test to validate the module.' + location: '' + lock: { + kind: 'None' + } + managedIdentities: { + systemAssigned: true + } + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "ltmax001" + }, + // Non-required parameters + "loadTestDescription": { + "value": "This is a test load test to validate the module." + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "None" + } + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +### Example 3: _Using Customer-Managed-Keys with User-Assigned identity_ + +This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. + + +

+ +via Bicep module + +```bicep +module loadTest 'br/public:avm/res/load-test-service/load-test:' = { + name: 'loadTestDeployment' + params: { + // Required parameters + name: 'ltucmk001' + // Non-required parameters + customerManagedKey: { + keyName: '' + keyVaultResourceId: '' + userAssignedIdentityResourceId: '' + } + location: '' + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "ltucmk001" + }, + // Non-required parameters + "customerManagedKey": { + "value": { + "keyName": "", + "keyVaultResourceId": "", + "userAssignedIdentityResourceId": "" + } + }, + "location": { + "value": "" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module loadTest 'br/public:avm/res/load-test-service/load-test:' = { + name: 'loadTestDeployment' + params: { + // Required parameters + name: 'ltwaf001' + // Non-required parameters + enableTelemetry: '' + loadTestDescription: 'This is a sample load test.' + location: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "ltwaf001" + }, + // Non-required parameters + "enableTelemetry": { + "value": "" + }, + "loadTestDescription": { + "value": "This is a sample load test." + }, + "location": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the Load test. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`loadTestDescription`](#parameter-loadtestdescription) | string | The description of the load test. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Resource tags. | + +### Parameter: `name` + +Name of the Load test. + +- Required: Yes +- Type: string + +### Parameter: `customerManagedKey` + +The customer managed key definition. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyName`](#parameter-customermanagedkeykeyname) | string | The name of the customer managed key to use for encryption. | +| [`keyVaultResourceId`](#parameter-customermanagedkeykeyvaultresourceid) | string | The resource ID of a key vault to reference a customer managed key for encryption from. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVersion`](#parameter-customermanagedkeykeyversion) | string | The version of the customer managed key to reference for encryption. If not provided, using 'latest'. | +| [`userAssignedIdentityResourceId`](#parameter-customermanagedkeyuserassignedidentityresourceid) | string | User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. | + +### Parameter: `customerManagedKey.keyName` + +The name of the customer managed key to use for encryption. + +- Required: No +- Type: string + +### Parameter: `customerManagedKey.keyVaultResourceId` + +The resource ID of a key vault to reference a customer managed key for encryption from. + +- Required: No +- Type: string + +### Parameter: `customerManagedKey.keyVersion` + +The version of the customer managed key to reference for encryption. If not provided, using 'latest'. + +- Required: No +- Type: string + +### Parameter: `customerManagedKey.userAssignedIdentityResourceId` + +User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use. + +- Required: No +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `loadTestDescription` + +The description of the load test. + +- Required: No +- Type: string + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption. + +- Required: No +- Type: array + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `tags` + +Resource tags. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the load test. | +| `resourceGroupName` | string | The resource group the load test was deployed into. | +| `resourceId` | string | The resource ID of the load test. | +| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/load-test-service/load-test/main.bicep b/avm/res/load-test-service/load-test/main.bicep new file mode 100644 index 0000000000..cb5a1dd4c8 --- /dev/null +++ b/avm/res/load-test-service/load-test/main.bicep @@ -0,0 +1,242 @@ +metadata name = 'Load Testing Service' +metadata description = 'This module deploys a Load test.' +metadata owner = 'Azure/module-maintainers' + +// ================ // +// Parameters // +// ================ // + +@description('Required. Name of the Load test.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Resource tags.') +param tags object? + +@description('Optional. The managed identity definition for this resource.') +param managedIdentities managedIdentitiesType + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. The description of the load test.') +param loadTestDescription string? + +@description('Optional. The customer managed key definition.') +param customerManagedKey customerManagedKeyType? + +// =========== // +// Variables // +// =========== // + +var formattedUserAssignedIdentities = reduce( + map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), + {}, + (cur, next) => union(cur, next) +) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) + ? { + type: (managedIdentities.?systemAssigned ?? false) + ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') + : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None') + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +// ================ // +// Resources // +// ================ // + +resource loadTest_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: loadTest + } + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.loadtestservice-loadtest.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = + if (!empty(customerManagedKey.?keyVaultResourceId)) { + name: last(split((customerManagedKey.?keyVaultResourceId ?? 'dummyVault'), '/')) + scope: resourceGroup( + split((customerManagedKey.?keyVaultResourceId ?? '//'), '/')[2], + split((customerManagedKey.?keyVaultResourceId ?? '////'), '/')[4] + ) + + resource cMKKey 'keys@2023-02-01' existing = + if (!empty(customerManagedKey.?keyVaultResourceId) && !empty(customerManagedKey.?keyName)) { + name: customerManagedKey.?keyName ?? 'dummyKey' + } + } + +resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = + if (!empty(customerManagedKey.?userAssignedIdentityResourceId)) { + name: last(split(customerManagedKey.?userAssignedIdentityResourceId ?? 'dummyMsi', '/')) + scope: resourceGroup( + split((customerManagedKey.?userAssignedIdentityResourceId ?? '//'), '/')[2], + split((customerManagedKey.?userAssignedIdentityResourceId ?? '////'), '/')[4] + ) + } + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + identity: identity + tags: tags + properties: { + description: loadTestDescription + encryption: !empty(customerManagedKey) + ? { + identity: { + type: 'UserAssigned' + resourceId: !empty(customerManagedKey.?userAssignedIdentityResourceId) ? cMKUserAssignedIdentity.id : null + } + keyUrl: !empty(customerManagedKey.?keyVersion ?? '') + ? '${cMKKeyVault::cMKKey.properties.keyUri}/${customerManagedKey!.keyVersion}' + : cMKKeyVault::cMKKey.properties.keyUriWithVersion + } + : null + } +} + +resource loadTest_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(loadTest.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: loadTest + } +] + +// ================ // +// Outputs // +// ================ // + +@description('The name of the load test.') +output name string = loadTest.name + +@description('The resource ID of the load test.') +output resourceId string = loadTest.id + +@description('The resource group the load test was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = loadTest.location + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = loadTest.?identity.?principalId ?? '' + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @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? + + @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? +}[]? + +type customerManagedKeyType = { + @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') + userAssignedIdentityResourceId: string? + + @description('Required. The resource ID of a key vault to reference a customer managed key for encryption from.') + keyVaultResourceId: string? + + @description('Required. The name of the customer managed key to use for encryption.') + keyName: string? + + @description('Optional. The version of the customer managed key to reference for encryption. If not provided, using \'latest\'.') + keyVersion: string? +}? + +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? + + @description('Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption.') + userAssignedResourceIds: string[]? +}? diff --git a/avm/res/load-test-service/load-test/main.json b/avm/res/load-test-service/load-test/main.json new file mode 100644 index 0000000000..db96fce11b --- /dev/null +++ b/avm/res/load-test-service/load-test/main.json @@ -0,0 +1,378 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "7118073794250229597" + }, + "name": "Load Testing Service", + "description": "This module deploys a Load test.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + }, + "keyVaultResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Load test." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "loadTestDescription": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the load test." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "loadTest_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.LoadTestService/loadTests/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "loadTest" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.loadtestservice-loadtest.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "loadTest": { + "type": "Microsoft.LoadTestService/loadTests", + "apiVersion": "2022-12-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('loadTestDescription')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('identity', createObject('type', 'UserAssigned', 'resourceId', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null())), 'keyUrl', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion)), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "loadTest_roleAssignments": { + "copy": { + "name": "loadTest_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.LoadTestService/loadTests/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.LoadTestService/loadTests', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "loadTest" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the load test." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the load test." + }, + "value": "[resourceId('Microsoft.LoadTestService/loadTests', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the load test was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('loadTest', '2022-12-01', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('loadTest', '2022-12-01', 'full'), 'identity'), 'principalId'), '')]" + } + } +} \ No newline at end of file diff --git a/avm/res/load-test-service/load-test/tests/e2e/defaults/main.test.bicep b/avm/res/load-test-service/load-test/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..9b031cc77d --- /dev/null +++ b/avm/res/load-test-service/load-test/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,45 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-loadTestService.loadTest-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ltmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + } +} diff --git a/avm/res/load-test-service/load-test/tests/e2e/max/dependencies.bicep b/avm/res/load-test-service/load-test/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..0b7f226c1a --- /dev/null +++ b/avm/res/load-test-service/load-test/tests/e2e/max/dependencies.bicep @@ -0,0 +1,16 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep b/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..e2a7ec1328 --- /dev/null +++ b/avm/res/load-test-service/load-test/tests/e2e/max/main.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-loadTestService.loadTest-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ltmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + lock: { + kind: 'None' + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + managedIdentities: { + systemAssigned: true + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + loadTestDescription: 'This is a test load test to validate the module.' + } +} diff --git a/avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep b/avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep new file mode 100644 index 0000000000..8ebced9503 --- /dev/null +++ b/avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/dependencies.bicep @@ -0,0 +1,64 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Key-Vault-Crypto-User-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '12338af0-0e69-4776-bea7-57ae8d297424' + ) // Key Vault Crypto User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created Key Vault encryption key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The URL of the created Key Vault.') +output keyVaultKeyUrl string = keyVault::key.properties.keyUriWithVersion + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/main.test.bicep b/avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/main.test.bicep new file mode 100644 index 0000000000..e50e444333 --- /dev/null +++ b/avm/res/load-test-service/load-test/tests/e2e/user-assigned-cmk-encryption/main.test.bicep @@ -0,0 +1,69 @@ +targetScope = 'subscription' + +metadata name = 'Using Customer-Managed-Keys with User-Assigned identity' +metadata description = 'This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-loadTestService.loadTest-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ltucmk' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + location: resourceLocation + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + customerManagedKey: { + keyName: nestedDependencies.outputs.keyVaultKeyName + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + userAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + } + } +} diff --git a/avm/res/load-test-service/load-test/tests/e2e/waf-aligned/main.test.bicep b/avm/res/load-test-service/load-test/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..1134a5b323 --- /dev/null +++ b/avm/res/load-test-service/load-test/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,55 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-loadTestService.loadTest-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ltwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableTelemetry bool = true + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + enableTelemetry: enableTelemetry + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + loadTestDescription: 'This is a sample load test.' + } +} diff --git a/avm/res/load-test-service/load-test/version.json b/avm/res/load-test-service/load-test/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/load-test-service/load-test/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 66cafb374e63f7d63730bcc02c31256c199f6b1a Mon Sep 17 00:00:00 2001 From: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:49:24 +0100 Subject: [PATCH 37/66] fix: Dnsrule bug for virtual Network link name (#1538) ## Description update to virualtNetworkLinkName Closes #1463 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.dns-forwarding-ruleset](https://github.com/ChrisSidebotham/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml/badge.svg?branch=dnsrule-bug-1463)](https://github.com/ChrisSidebotham/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../network/dns-forwarding-ruleset/README.md | 44 ++++++++++++++++--- .../network/dns-forwarding-ruleset/main.bicep | 15 +++++-- .../network/dns-forwarding-ruleset/main.json | 36 ++++++++++++--- .../tests/e2e/max/main.test.bicep | 7 ++- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/avm/res/network/dns-forwarding-ruleset/README.md b/avm/res/network/dns-forwarding-ruleset/README.md index 0f68d737bc..74096309a7 100644 --- a/avm/res/network/dns-forwarding-ruleset/README.md +++ b/avm/res/network/dns-forwarding-ruleset/README.md @@ -148,8 +148,11 @@ module dnsForwardingRuleset 'br/public:avm/res/network/dns-forwarding-ruleset:' + virtualNetworkLinks: [ + { + name: 'mytestvnetlink1' + virtualNetworkResourceId: '' + } ] } } @@ -227,9 +230,12 @@ module dnsForwardingRuleset 'br/public:avm/res/network/dns-forwarding-ruleset:" + { + "name": "mytestvnetlink1", + "virtualNetworkResourceId": "" + } ] } } @@ -337,7 +343,7 @@ module dnsForwardingRuleset 'br/public:avm/res/network/dns-forwarding-ruleset: Date: Mon, 15 Apr 2024 16:54:00 +0200 Subject: [PATCH 38/66] feat: Convert publishModuleIndex to PWSH (#1533) ## Description [AB#33918](https://dev.azure.com/CSUSolEng/Azure%20Verified%20Modules/_workitems/edit/33918) This pull request includes changes to the bicep module index JSON file generation process. The primary changes include the introduction of a new GitHub workflow and PowerShell script to generate the module index and the removal of the previous workflow and JavaScript script. The new workflow is scheduled to run daily at 3:45 AM PST and uses Azure login and PowerShell to generate and upload the module index to Azure blob storage. The new PowerShell script generates the module index based on the modules in the repository and merges it with the previous version of the index. > Diffs have been compared and apart from a move of `avm/res/app-configuration/configuration-store` in the JSON file as its being sorted differently, there are no regressions. You can check for yourself in VScode by downloading, saving and comparing the 2 files from: [current workflow](https://biceplivedatasaprod.blob.core.windows.net/bicep-cdn-live-data-container/module-index) VS [new workflow](https://stguksjtbrmindextest.blob.core.windows.net/bicep-cdn-live-data-container/module-index) Changes to GitHub workflows: * [`.github/workflows/avm.platform.publish-module-index-json.yml`](diffhunk://#diff-a32f4d20d0b1b49f6307c794b2b75c9515a36131eef33ccab3012a2c3c431ae3R1-R77): Added a new GitHub workflow that is scheduled to run daily at 3:45 AM PST. This workflow checks out the repository, logs in to Azure, installs Azure PowerShell Modules, generates the module index, uploads the artifacts, and uploads the module index to Azure blob storage. * [`.github/workflows/publish-module-index.yml`](diffhunk://#diff-0db9403318bc32b133eb4c1f5af094a003d067d3cce1e5839b773910d8536cb0L1-L118): Removed the previous GitHub workflow that was used to publish the module index. Changes to scripts: * [`avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1`](diffhunk://#diff-d93730e5546a51dc8ca6236af545822fff02a6b4b551eb6d0a34ac4e457446a1R1-R169): Added a new PowerShell script that generates the module index based on the modules in the repository and merges it with the previous version of the index. This script is used in the new GitHub workflow. * [`scripts/github-actions/generate-module-index-data.js`](diffhunk://#diff-2c75227ba0b424f38faa9fe12c0fe64333ce433bbca33986e000139d8445c399L1-L211): Removed the previous JavaScript script that was used to generate the module index. ## Pipeline Reference | Pipeline | | -------- | | [![avm.platform.publish-module-index-json](https://github.com/jtracey93/bicep-registry-modules/actions/workflows/avm.platform.publish-module-index-json.yml/badge.svg)](https://github.com/jtracey93/bicep-registry-modules/actions/workflows/avm.platform.publish-module-index-json.yml) | > Had to merge in main to resolve a conflict which wiped the workflow from my fork. However, last run can be seen here: https://github.com/jtracey93/bicep-registry-modules/actions/runs/8572556792/job/23495383080 and comparison of files above in storage accounts is sufficient evidence ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Alexander Sehr --- ...avm.platform.publish-module-index-json.yml | 95 ++++++++ .github/workflows/publish-module-index.yml | 118 ---------- .gitignore | 2 + .../Invoke-AvmJsonModuleIndexGeneration.ps1 | 222 ++++++++++++++++++ .../generate-module-index-data.js | 211 ----------------- .../generate-module-index-md.js | 179 -------------- .../runlocally/run-generate-module-index.bat | 4 - .../runlocally/run-generate-module-index.js | 50 ---- .../runlocally/run-generate-module-index.sh | 9 - 9 files changed, 319 insertions(+), 571 deletions(-) create mode 100644 .github/workflows/avm.platform.publish-module-index-json.yml delete mode 100644 .github/workflows/publish-module-index.yml create mode 100644 avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 delete mode 100644 scripts/github-actions/generate-module-index-data.js delete mode 100644 scripts/github-actions/generate-module-index-md.js delete mode 100644 scripts/github-actions/runlocally/run-generate-module-index.bat delete mode 100644 scripts/github-actions/runlocally/run-generate-module-index.js delete mode 100644 scripts/github-actions/runlocally/run-generate-module-index.sh diff --git a/.github/workflows/avm.platform.publish-module-index-json.yml b/.github/workflows/avm.platform.publish-module-index-json.yml new file mode 100644 index 0000000000..d82f31317e --- /dev/null +++ b/.github/workflows/avm.platform.publish-module-index-json.yml @@ -0,0 +1,95 @@ +# This publishes the list of all public bicep modules to an index file that the Bicep vscode extension can read for intellisense using pwsh +name: .Platform - Publish [moduleIndex.json] +on: + schedule: + - cron: 45 11 * * * # Run daily at 3:45 AM PST + workflow_dispatch: + +permissions: + id-token: write + contents: read + +jobs: + upload-index-data: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 // Needed to fetch all history and tags + + - name: Log in to Azure + uses: azure/login@v2 + with: + client-id: ${{ secrets.PUBLISH_CLIENT_ID }} + tenant-id: ${{ secrets.PUBLISH_TENANT_ID }} + subscription-id: ${{ secrets.PUBLISH_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Install Azure Powershell Modules + shell: pwsh + run: | + if(-not (Get-Module 'Az.Storage' -ListAvailable)) { + Install-Module -Name 'Az.Storage' -Force + } + + - name: Generate moduleIndex.json + shell: pwsh + run: | + # Load used functions + . (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'pipelines' 'platform' 'Invoke-AvmJsonModuleIndexGeneration.ps1') + + $functionInput = @{ + storageAccountName = 'biceplivedatasaprod' + storageAccountContainer = 'bicep-cdn-live-data-container' + storageBlobName = 'module-index' + moduleIndexJsonFilePath = 'moduleIndex.json' + prefixForLastModuleIndexJsonFile = 'last-' + prefixForCurrentGeneratedModuleIndexJsonFile = 'generated-' + } + + Write-Verbose "Invoke task with" -Verbose + Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose + + if(-not (Invoke-AvmJsonModuleIndexGeneration @functionInput -Force)) { + Write-Output ('{0}={1}' -f 'anyErrorsOccurred', $true) >> $env:GITHUB_ENV + } + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: publish-module-index-json-artifacts + path: | + moduleIndex.json + last-moduleIndex.json + generated-moduleIndex.json + + - name: Upload to blob storage + shell: pwsh + run: | + $storageAccountInfo = @{ + storageAccountName = 'biceplivedatasaprod' + storageAccountContainer = 'bicep-cdn-live-data-container' + storageBlobName = 'module-index' + storageBlobContentType = @{'ContentType' = 'application/json'} + } + Write-Verbose ('Uploading [moduleIndex.json] to blob storage account [{0}] in container [{1}] as blob [{2}]' -f $storageAccountInfo.storageAccountName, $storageAccountInfo.storageAccountContainer, $storageAccountInfo.storageBlobName) -Verbose + + $storageContext = New-AzStorageContext -StorageAccountName $storageAccountInfo.storageAccountName -UseConnectedAccount + + $functionInput = @{ + Context = $storageContext + Container = $storageAccountInfo.storageAccountContainer + Blob = $storageAccountInfo.storageBlobName + File = 'moduleIndex.json' + Properties = $storageAccountInfo.storageBlobContentType + } + Set-AzStorageBlobContent @functionInput -Force + + Write-Verbose ('Upload of [{0}] complete.' -f $storageAccountInfo.storageBlobName) -Verbose + + - name: Check if any errors occurred during 'Generate moduleIndex.json' + if: ${{ env.anyErrorsOccurred == 'true' }} + shell: pwsh + run: | + throw "Errors occurred during 'Generate moduleIndex.json' step. Please check the logs of that step in the workflow." diff --git a/.github/workflows/publish-module-index.yml b/.github/workflows/publish-module-index.yml deleted file mode 100644 index bbf1b68164..0000000000 --- a/.github/workflows/publish-module-index.yml +++ /dev/null @@ -1,118 +0,0 @@ -# This publishes the list of all public bicep modules to an index file that the Bicep vscode extension can read for intellisense -# and also a human-readable HTML version. -name: Publish module index -on: - schedule: - - cron: 45 11 * * * # Run daily at 3:45 AM PST - workflow_dispatch: - -permissions: - id-token: write - pages: write - contents: read - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - upload-index-data: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 // Needed to fetch all history and tags - - - name: Install packages - run: npm ci - - - name: Generate moduleIndex.json - uses: actions/github-script@v7 - with: - script: | - const script = require("./scripts/github-actions/generate-module-index-data.js") - await script({ require, github, context, core }) - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: moduleIndex.json - path: moduleIndex.json - - - name: Log in to Azure - uses: azure/login@v2 - with: - client-id: ${{ secrets.PUBLISH_CLIENT_ID }} - tenant-id: ${{ secrets.PUBLISH_TENANT_ID }} - subscription-id: ${{ secrets.PUBLISH_SUBSCRIPTION_ID }} - - - name: Upload to blob storage - uses: azure/CLI@v2 - with: - inlineScript: | - az storage blob upload --account-name biceplivedatasaprod --container-name bicep-cdn-live-data-container --name module-index --file moduleIndex.json --auth-mode login --overwrite - - build-index-page: - runs-on: ubuntu-latest - needs: upload-index-data - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install packages - run: npm ci - - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: moduleIndex.json - - - name: Generate index.md - uses: actions/github-script@v7 - with: - script: | - const script = require("./scripts/github-actions/generate-module-index-md.js") - await script({ require, github, context, core }) - - - name: Upload Markdown artifact - uses: actions/upload-artifact@v4 - with: - path: ./docs/jekyll/index.md - name: index.md - - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: "3.1" - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - cache-version: 0 # Increment this number if you need to re-download cached gems - working-directory: ./docs/jekyll - - - name: Setup Pages - id: pages - uses: actions/configure-pages@v5 - - - name: Build with Jekyll - working-directory: ./docs/jekyll - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" # Outputs to the './_site' directory by default - env: - JEKYLL_ENV: production - - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./docs/jekyll/_site - - deploy-index-page: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build-index-page - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 813cba8c38..f73a7ea35c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ node_modules .netrc moduleIndex.json +last-moduleIndex.json +generated-moduleIndex.json .DS_Store diff --git a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 new file mode 100644 index 0000000000..49a58895c5 --- /dev/null +++ b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 @@ -0,0 +1,222 @@ +<# +.SYNOPSIS +Creates the moduleIndex.json file for the AVM modules that is used by Visual Studio Code and other IDEs to provide the intellisense list of modules from the Bicep public registry. + +.PARAMETER storageAccountName +The name of the Azure Storage Account where the moduleIndex.json file is stored. Default is 'biceplivedatasaprod'. + +.PARAMETER storageAccountContainer +The name of the Azure Storage Account Blob Container where the moduleIndex.json file is stored. Default is 'bicep-cdn-live-data-container'. + +.PARAMETER storageBlobName +The name of the Azure Storage Account Blob where the moduleIndex.json file is stored. Default is 'module-index'. + +.PARAMETER moduleIndexJsonFilePath +The file path to save the moduleIndex.json file to. Default is 'moduleIndex.json'. + +.PARAMETER prefixForLastModuleIndexJsonFile +The prefix to add to the last version of the moduleIndex.json file that is downloaded from the storage account. Default is 'last-'. + +.PARAMETER prefixForCurrentGeneratedModuleIndexJsonFile +The prefix to add to the current generated moduleIndex.json file. Default is 'generated-'. + +.PARAMETER doNotMergeWithLastModuleIndexJsonFileVersion +If specified, the last version of the moduleIndex.json file that is downloaded from the storage account will not be merged with the current generated moduleIndex.json file. + +.DESCRIPTION +Creates the moduleIndex.json file for the AVM modules that is used by Visual Studio Code and other IDEs to provide the intellisense list of modules from the Bicep public registry. + +Also has error handling to cope with a module not being published fully but will not prevent the script from completeing each time. + +The script uses a merging strategy with the previous version of moduleIndex.json to ensure that the file is always up to date with the latest modules but previous versions are not removed, this can be changed by specifying the $doNotMergeWithLastModuleIndexJsonFileVersion parameter. + +.EXAMPLE +Invoke-AvmJsonModuleIndexGeneration -storageAccountName '' -storageAccountContainer '' -storageBlobName '' -moduleIndexJsonFilePath 'moduleIndex.json' -prefixForLastModuleIndexJsonFile 'last-' -prefixForCurrentGeneratedModuleIndexJsonFile 'generated-' + +This example will generate the moduleIndex.json file for the AVM modules and save it to the current directory and merge it with the last version of the moduleIndex.json file that was downloaded from the storage account. + +.NOTES +The function requires Azure PowerShell Storage Module (Az.Storage) to be installed and the user to be logged in to Azure. +#> + +function Invoke-AvmJsonModuleIndexGeneration { + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $false)] + [string] $storageAccountName = 'biceplivedatasaprod', + + [Parameter(Mandatory = $false)] + [string] $storageAccountContainer = 'bicep-cdn-live-data-container', + + [Parameter(Mandatory = $false)] + [string] $storageBlobName = 'module-index', + + [Parameter(Mandatory = $false)] + [string] $moduleIndexJsonFilePath = 'moduleIndex.json', + + [Parameter(Mandatory = $false)] + [string] $prefixForLastModuleIndexJsonFile = 'last-', + + [Parameter(Mandatory = $false)] + [string] $prefixForCurrentGeneratedModuleIndexJsonFile = 'generated-', + + [Parameter(Mandatory = $false)] + [switch] $doNotMergeWithLastModuleIndexJsonFileVersion + ) + + ## Download the current published moduleIndex.json from the storage account if the $doNotMergeWithLastModuleIndexJsonFileVersion is set to $false + if (-not $doNotMergeWithLastModuleIndexJsonFileVersion) { + try { + $lastModuleIndexJsonFilePath = $prefixForLastModuleIndexJsonFile + $moduleIndexJsonFilePath + + Write-Verbose "Attempting to get last version of the moduleIndex.json from the Storage Account: $storageAccountName, Container: $storageAccountContainer, Blob: $storageBlobName and save to file: $lastModuleIndexJsonFilePath ..." -Verbose + + $storageContext = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount + + Get-AzStorageBlobContent -Blob $storageBlobName -Container $storageAccountContainer -Context $storageContext -Destination $lastModuleIndexJsonFilePath -Force | Out-Null + } catch { + Write-Error "Unable to retrieve moduleIndex.json file from the Storage Account: $storageAccountName, Container: $storageAccountContainer, Blob: $storageBlobName. Error: $($_.Exception.Message)" -ErrorAction Stop + } + + ## Check if the last version of the moduleIndex.json (last-moduleIndex.json) file exists and is not empty + + if (Test-Path $lastModuleIndexJsonFilePath) { + $lastModuleIndexJsonFileContent = Get-Content $lastModuleIndexJsonFilePath + if ($null -eq $lastModuleIndexJsonFileContent) { + Write-Error "The last version of the moduleIndex.json file (last-moduleIndex.json) exists but is empty. File: $lastModuleIndexJsonFilePath" -ErrorAction Stop + } + Write-Verbose 'The last version of the moduleIndex.json file (last-moduleIndex.json) exists and is not empty. Proceeding...' -Verbose + } + } + + ## Generate the new moduleIndex.json file based off the modules in the repository + + $currentGeneratedModuleIndexJsonFilePath = $prefixForCurrentGeneratedModuleIndexJsonFile + $moduleIndexJsonFilePath + + Write-Verbose "Generating the current generated moduleIndex.json file and saving to: $currentGeneratedModuleIndexJsonFilePath ..." -Verbose + + $anyErrorsOccurred = $false + $moduleIndexData = @() + + foreach ($avmModuleRoot in @('avm/res', 'avm/ptn')) { + $avmModuleGroups = (Get-ChildItem -Path $avmModuleRoot -Directory).Name + + foreach ($moduleGroup in $avmModuleGroups) { + $moduleGroupPath = "$avmModuleRoot/$moduleGroup" + $moduleNames = (Get-ChildItem -Path $moduleGroupPath -Directory).Name + + foreach ($moduleName in $moduleNames) { + $modulePath = "$moduleGroupPath/$moduleName" + $mainJsonPath = "$modulePath/main.json" + $tagListUrl = "https://mcr.microsoft.com/v2/bicep/$modulePath/tags/list" + + try { + Write-Verbose "Processing AVM Module '$modulePath'..." -Verbose + Write-Verbose " Getting available tags at '$tagListUrl'..." -Verbose + + try { + $tagListResponse = Invoke-RestMethod -Uri $tagListUrl + } catch { + $anyErrorsOccurred = $true + Write-Error "Error occurred while accessing URL: $tagListUrl" + Write-Error "Error message: $($_.Exception.Message)" + continue + } + $tags = $tagListResponse.tags | Sort-Object + + $properties = [ordered]@{} + foreach ($tag in $tags) { + $gitTag = "$modulePath/$tag" + $documentationUri = "https://github.com/Azure/bicep-registry-modules/tree/$gitTag/$modulePath/README.md" + + try { + $moduleMainJsonUri = "https://raw.githubusercontent.com/Azure/bicep-registry-modules/$gitTag/$mainJsonPath" + Write-Verbose " Getting available description for tag $tag via '$moduleMainJsonUri'..." -Verbose + $moduleMainJsonUriResponse = Invoke-RestMethod -Uri $moduleMainJsonUri + $description = $moduleMainJsonUriResponse.metadata.description + } catch { + $anyErrorsOccurred = $true + Write-Error "Error occurred while accessing description for tag $tag via '$moduleMainJsonUri'" + Write-Error "Error message: $($_.Exception.Message)" + continue + } + + $properties[$tag] = [ordered]@{ + description = $description + documentationUri = $documentationUri + } + } + + $moduleIndexData += [ordered]@{ + moduleName = $modulePath + tags = @($tags) + properties = $properties + } + } catch { + $anyErrorsOccurred = $true + Write-Error "Error message: $($_.Exception.Message)" + } + } + + $numberOfModuleGroupsProcessed++ + } + } + + Write-Verbose "Processed $numberOfModuleGroupsProcessed modules groups." -Verbose + Write-Verbose "Processed $($moduleIndexData.Count) total modules." -Verbose + + Write-Verbose "Convert moduleIndexData variable to JSON and save as 'generated-moduleIndex.json'" -Verbose + $moduleIndexData | ConvertTo-Json -Depth 10 | Out-File -FilePath $currentGeneratedModuleIndexJsonFilePath + + ## Merge the new moduleIndex.json file with the previous version if the $doNotMergeWithLastModuleIndexJsonFileVersion is not specified + + if (-not $doNotMergeWithLastModuleIndexJsonFileVersion) { + Write-Verbose "Merging 'generated-moduleIndex.json' (new) file with 'last-moduleIndex.json' (previous) file..." -Verbose + + $lastModuleIndexJsonFileContent = Get-Content $lastModuleIndexJsonFilePath + $currentGeneratedModuleIndexJsonFileContent = Get-Content $currentGeneratedModuleIndexJsonFilePath + + $lastModuleIndexData = $lastModuleIndexJsonFileContent | ConvertFrom-Json -Depth 10 + $currentGeneratedModuleIndexData = $currentGeneratedModuleIndexJsonFileContent | ConvertFrom-Json -Depth 10 + + $initialMergeOfJsonFilesData = @{} + + foreach ($module in $currentGeneratedModuleIndexData) { + $initialMergeOfJsonFilesData[$module.moduleName] = $module + } + + # Add modules from lastModuleIndexData to the initialMergeOfJsonFilesData hashtable, merging tags and properties if they exist in both files + foreach ($module in $lastModuleIndexData) { + if (-not $initialMergeOfJsonFilesData.ContainsKey($module.moduleName)) { + $initialMergeOfJsonFilesData[$module.moduleName] = $module + } else { + # If the module exists, merge the tags and properties + $mergedModule = $initialMergeOfJsonFilesData[$module.moduleName] + $mergedModule.tags = @(($mergedModule.tags + $module.tags) | Sort-Object -Unique) + + # Merge properties + foreach ($property in $module.properties.PSObject.Properties) { + if (-not $mergedModule.properties.PSObject.Properties.Name.Contains($property.Name)) { + $mergedModule.properties | Add-Member -NotePropertyName $property.Name -NotePropertyValue $property.Value + } + } + } + } + + # Convert the mergedModuleIndexData hashtable to an array of values (i.e., the modules) + $mergedModuleIndexData = $initialMergeOfJsonFilesData.Values + + # Sort the modules by their names + $sortedMergedModuleIndexData = $mergedModuleIndexData | Sort-Object moduleName + + Write-Verbose "Convert mergedModuleIndexData variable to JSON and save as 'moduleIndex.json'" -Verbose + $sortedMergedModuleIndexData | ConvertTo-Json -Depth 10 | Out-File -FilePath $moduleIndexJsonFilePath + } + if ($doNotMergeWithLastModuleIndexJsonFileVersion -eq $true) { + Write-Verbose "Convert currentGeneratedModuleIndexData variable to JSON and save as 'moduleIndex.json to overwrite it as `doNotMergeWithLastModuleIndexJsonFileVersion` was specified'" -Verbose + $moduleIndexData | ConvertTo-Json -Depth 10 | Out-File -FilePath $moduleIndexJsonFilePath -Force + } + + return ($anyErrorsOccurred ? $false : $true) + +} diff --git a/scripts/github-actions/generate-module-index-data.js b/scripts/github-actions/generate-module-index-data.js deleted file mode 100644 index 0489b525b1..0000000000 --- a/scripts/github-actions/generate-module-index-data.js +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @param {typeof import("fs").promises} fs - * @param {string} dir - */ -async function getSubdirNames(fs, dir) { - var files = await fs.readdir(dir, { withFileTypes: true }); - return files.filter((x) => x.isDirectory()).map((x) => x.name); -} - -async function getModuleDescription( - github, - core, - mainJsonPath, - gitTag, - context -) { - const gitTagRef = `tags/${gitTag}`; - - core.info(` Retrieving main.json at Git tag ref ${gitTagRef}`); - - // Get the SHA of the commit - const { - data: { - object: { sha: commitSha }, - }, - } = await github.rest.git.getRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: gitTagRef, - }); - - // Get the tree data - const { - data: { tree }, - } = await github.rest.git.getTree({ - owner: context.repo.owner, - repo: context.repo.repo, - tree_sha: commitSha, - recursive: true, - }); - - // Find the file in the tree - const file = tree.find((f) => f.path === mainJsonPath); - if (!file) { - throw new Error(`File ${mainJsonPath} not found in repository`); - } - - // Get the blob data - const { - data: { content }, - } = await github.rest.git.getBlob({ - owner: context.repo.owner, - repo: context.repo.repo, - file_sha: file.sha, - }); - - // content is base64 encoded, so decode it - const fileContent = Buffer.from(content, "base64").toString("utf8"); - - // Parse the main.json file - if (fileContent !== "") { - const json = JSON.parse(fileContent); - return json.metadata.description; - } else { - throw new Error( - "The specified path does not represent a file or it is empty." - ); - } -} - -/** - * @typedef Params - * @property {typeof require} require - * @property {ReturnType} github - * @property {typeof import("@actions/github").context} context - * @property {typeof import("@actions/core")} core - * - * @param {Params} params - */ -async function generateModuleIndexData({ require, github, context, core }) { - const fs = require("fs").promises; - const { existsSync } = require("fs"); - const axios = require("axios").default; - const moduleIndexData = []; - - let numberOfModuleGroupsProcessed = 0; - - // BRM Modules - for (const moduleGroup of await getSubdirNames(fs, "modules")) { - const moduleGroupPath = `modules/${moduleGroup}`; - const moduleNames = await getSubdirNames(fs, moduleGroupPath); - - for (const moduleName of moduleNames) { - const modulePath = `${moduleGroupPath}/${moduleName}`; - const archivedFilePath = `${modulePath}/ARCHIVED.md`; - - if (existsSync(archivedFilePath)) { - continue; - } - - const mainJsonPath = `${modulePath}/main.json`; - // BRM module git tags do not include the modules/ prefix. - const mcrModulePath = modulePath.slice(8); - const tagListUrl = `https://mcr.microsoft.com/v2/bicep/${mcrModulePath}/tags/list`; - - try { - core.info(`Processing BRM Module "${modulePath}"...`); - core.info(` Getting available tags at "${tagListUrl}"...`); - - const tagListResponse = await axios.get(tagListUrl); - const tags = tagListResponse.data.tags.sort(); - - const properties = {}; - for (const tag of tags) { - // Using mcrModulePath because BRM module git tags do not include the modules/ prefix - const gitTag = `${mcrModulePath}/${tag}`; - const documentationUri = `https://github.com/Azure/bicep-registry-modules/tree/${gitTag}/${modulePath}/README.md`; - const description = await getModuleDescription( - github, - core, - mainJsonPath, - gitTag, - context - ); - - properties[tag] = { description, documentationUri }; - } - - moduleIndexData.push({ - moduleName: mcrModulePath, - tags, - properties, - }); - } catch (error) { - core.setFailed(error); - } - } - - numberOfModuleGroupsProcessed++; - } - - for (const avmModuleRoot of ["avm/res", "avm/ptn"]) { - // Resource module path pattern: `avm/res/${moduleGroup}/${moduleName}` - // Pattern module path pattern: `avm/ptn/${moduleGroup}/${moduleName}` - const avmModuleGroups = await getSubdirNames(fs, avmModuleRoot); - - for (const moduleGroup of avmModuleGroups) { - const moduleGroupPath = `${avmModuleRoot}/${moduleGroup}`; - const moduleNames = await getSubdirNames(fs, moduleGroupPath); - - for (const moduleName of moduleNames) { - const modulePath = `${moduleGroupPath}/${moduleName}`; - const mainJsonPath = `${modulePath}/main.json`; - const tagListUrl = `https://mcr.microsoft.com/v2/bicep/${modulePath}/tags/list`; - - try { - core.info(`Processing AVM Module "${modulePath}"...`); - core.info(` Getting available tags at "${tagListUrl}"...`); - - const tagListResponse = await axios.get(tagListUrl); - const tags = tagListResponse.data.tags.sort(); - - const properties = {}; - for (const tag of tags) { - const gitTag = `${modulePath}/${tag}`; - const documentationUri = `https://github.com/Azure/bicep-registry-modules/tree/${gitTag}/${modulePath}/README.md`; - const description = await getModuleDescription( - github, - core, - mainJsonPath, - gitTag, - context - ); - - properties[tag] = { description, documentationUri }; - } - - moduleIndexData.push({ - moduleName: modulePath, - tags, - properties, - }); - } catch (error) { - core.setFailed(error); - } - } - - numberOfModuleGroupsProcessed++; - } - } - - core.info(`Writing moduleIndex.json`); - await fs.writeFile( - "moduleIndex.json", - JSON.stringify(moduleIndexData, null, 2) - ); - - core.info(`Processed ${numberOfModuleGroupsProcessed} modules groups.`); - core.info(`Processed ${moduleIndexData.length} total modules.`); - core.info( - `${ - moduleIndexData.filter((m) => - Object.keys(m.properties).some( - (key) => "description" in m.properties[key] - ) - ).length - } modules have a description` - ); -} - -module.exports = generateModuleIndexData; diff --git a/scripts/github-actions/generate-module-index-md.js b/scripts/github-actions/generate-module-index-md.js deleted file mode 100644 index 5c7bb1360d..0000000000 --- a/scripts/github-actions/generate-module-index-md.js +++ /dev/null @@ -1,179 +0,0 @@ -/** - * @param {object[]} items - * @param {(item: any) => string} keyGetter - * @returns - */ -function groupBy(items, keyGetter) { - const map = new Map(); - - for (const item of items) { - const key = keyGetter(item); - const collection = map.get(key); - if (!collection) { - map.set(key, [item]); - } else { - collection.push(item); - } - } - - return map; -} - -/** - * @param {ReturnType} github - * @param {typeof import("@actions/github").context} context - * @param {string} tag - */ -async function getPublishDate(github, context, tag) { - const { owner, repo } = context.repo; - - const reference = await github.rest.git.getRef({ - owner, - repo, - ref: `tags/${tag}`, - }); - if (!reference) { - throw `Could not find tag ${tag}`; - } - - const commit = await github.rest.git.getCommit({ - owner: context.repo.owner, - repo: context.repo.repo, - commit_sha: reference.data.object.sha, - }); - if (!commit) { - throw `Could not find commit for tag ${tag}`; - } - - return commit.data.committer.date.split("T")[0]; -} - -/** - * @param {ReturnType} github - * @param {typeof import("@actions/github").context} context - * @param {Array<{ moduleName: string, tags: string[] }>} modules - * @param {typeof import("prettier")} prettier - * @returns - */ -async function generateModuleGroupTable(github, context, modules, prettier) { - const moduleGroupTableData = [ - [ - "Module", - "Latest version", - "Published on", - "Source code", - "Readme", - "Description", - ], - ]; - - for (const module of modules) { - const modulePath = `\`${module.moduleName}\``; - - // module.tags is an sorted array. - const latestVersion = module.tags.slice(-1)[0]; - const versionListUrl = `https://mcr.microsoft.com/v2/bicep/${module.moduleName}/tags/list`; - const versionBadgeUrl = `https://img.shields.io/badge/mcr-${latestVersion}-blue`; - const versionBadge = ``; - - const tag = `${module.moduleName}/${latestVersion}`; - const publishDate = await getPublishDate(github, context, tag); - - const description = - module.properties && - module.properties[latestVersion]?.description?.replace(/\n|\r/g, " "); - - const moduleRootUrl = `https://github.com/Azure/bicep-registry-modules/tree/main/modules/${module.moduleName}`; - const sourceCodeButton = `[🦾 Source code](${moduleRootUrl}/main.bicep){: .btn}`; - const readmeButton = `[📃 Readme](${moduleRootUrl}/README.md){: .btn .btn-purple}`; - - moduleGroupTableData.push([ - modulePath, - versionBadge, - publishDate, - sourceCodeButton, - readmeButton, - description, - ]); - } - - const { markdownTable } = await import("markdown-table"); - const table = markdownTable(moduleGroupTableData, { - align: ["l", "r", "r", "r", "r", "l"], - }); - - return prettier.format(table, { parser: "markdown" }); -} - -/** - * @typedef Params - * @property {typeof require} require - * @property {ReturnType} github - * @property {typeof import("@actions/github").context} context - * @property {typeof import("@actions/core")} core - * - * @param {Params} params - */ -async function generateModuleIndexMarkdown({ require, github, context, core }) { - const fs = require("fs").promises; - const prettier = require("prettier"); - - var moduleIndexMarkdown = `--- -layout: default -title: Module Index -nav_order: 1 -permalink: / ---- - -# Module Index -{: .fs-9} - ---- - -{: .note-title } -> Azure Verified Modules (AVM) - Module Indexes -> -> The [Azure Verified Modules (AVM)](https://aka.ms/avm) module indexes are located over on the AVM website. You can find them here for: -> -> - [Resource Modules](https://aka.ms/avm/index/bicep/res) -> - [Pattern Modules](https://aka.ms/avm/index/bicep/ptn) - ---- - -`; - - const moduleIndexDataContent = await fs.readFile("moduleIndex.json", { - encoding: "utf-8", - }); - if (!moduleIndexDataContent) { - throw "Could not read moduleIndex.json"; - } - - const moduleIndexData = JSON.parse(moduleIndexDataContent); - const moduleGroups = groupBy( - moduleIndexData, - (x) => x.moduleName.split("/")[0] - ); - - for (const [moduleGroup, modules] of moduleGroups) { - if (moduleGroup.includes("avm")) { - continue; - } - core.debug(`Generating ${moduleGroup}...`); - - const moduleGroupTable = await generateModuleGroupTable( - github, - context, - modules, - prettier - ); - - moduleIndexMarkdown += `## ${moduleGroup}\n\n`; - moduleIndexMarkdown += moduleGroupTable; - moduleIndexMarkdown += "\n\n"; - } - - await fs.writeFile("docs/jekyll/index.md", moduleIndexMarkdown); -} - -module.exports = generateModuleIndexMarkdown; diff --git a/scripts/github-actions/runlocally/run-generate-module-index.bat b/scripts/github-actions/runlocally/run-generate-module-index.bat deleted file mode 100644 index 76c5608120..0000000000 --- a/scripts/github-actions/runlocally/run-generate-module-index.bat +++ /dev/null @@ -1,4 +0,0 @@ -rem See run-generate-module-index.js for instructions -rem Must run from the root of the repo - -node scripts\github-actions\runlocally\run-generate-module-index.js diff --git a/scripts/github-actions/runlocally/run-generate-module-index.js b/scripts/github-actions/runlocally/run-generate-module-index.js deleted file mode 100644 index e81e20630e..0000000000 --- a/scripts/github-actions/runlocally/run-generate-module-index.js +++ /dev/null @@ -1,50 +0,0 @@ -// This runs locally the scripts that power the "Publish Module Index" action on github -// (see bicep-registry-modules/.github/workflows/publish-module-index.yml) -// to make it easier to debug locally. -// -// Run via run-generate-module-index{.bat,.sh} from the root of the repo -// after having set up these environment variables: -// -// GITHUB_PAT: your github PAT -// GITHUB_OWNER: -// "Azure" for Azure/bicep-registry-modules -// sor your github username for a fork of bicep-registry-modules - -const path = require("path"); -const core = require("@actions/core"); - -const process = require("process"); -if (!process.env.GITHUB_PAT) { - console.error("Need to set GITHUB_PAT"); - return; -} -if (!process.env.GITHUB_OWNER) { - console.error( - 'Need to set GITHUB_OWNER (e.g. "Azure" for "Azure/bicep-registry-modules"' - ); - return; -} - -const github = require("@actions/github").getOctokit(process.env.GITHUB_PAT); - -const context = { - repo: { owner: process.env.GITHUB_OWNER, repo: "bicep-registry-modules" }, -}; - -const scriptGenerateModuleIndexData = require(path.join( - process.cwd(), - "scripts/github-actions/generate-module-index-data.js" -)); - -scriptGenerateModuleIndexData({ require, github, context, core }).then(() => { - const scriptGenerateModuleIndexMarkdown = require(path.join( - process.cwd(), - "scripts/github-actions/generate-module-index-md.js" - )); - - scriptGenerateModuleIndexMarkdown({ require, github, context, core }).then( - () => { - console.info("Done."); - } - ); -}); diff --git a/scripts/github-actions/runlocally/run-generate-module-index.sh b/scripts/github-actions/runlocally/run-generate-module-index.sh deleted file mode 100644 index 45210b4093..0000000000 --- a/scripts/github-actions/runlocally/run-generate-module-index.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -# See run-generate-module-index.js for instructions -# Must run from the root of the repo - -node scripts/github-actions/runlocally/run-generate-module-index.js -pushd docs/jekyll -bundle exec jekyll build --baseurl $PWD/_site -popd From edf3d5db6565488a943912b4963c3d3ec7fa948d Mon Sep 17 00:00:00 2001 From: John Date: Mon, 15 Apr 2024 17:00:06 +0200 Subject: [PATCH 39/66] feat: Added UDTs for disks and ability to give a custom name to a disk - `avm/res/compute/virtual-machine` (#1517) ## Description This pull request adds UDTs for OS disk and data disk. Also, included the option to give a custom name to either disk. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.compute.virtual-machine](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml/badge.svg?branch=johnlokerse%2Fissue1436)](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com> --- avm/res/compute/virtual-machine/README.md | 355 +++++++++++++++--- .../virtual-machine/extension/main.json | 4 +- avm/res/compute/virtual-machine/main.bicep | 110 ++++-- avm/res/compute/virtual-machine/main.json | 274 +++++++++++--- .../tests/e2e/atmg/main.test.bicep | 14 +- .../tests/e2e/linux.defaults/main.test.bicep | 2 +- .../tests/e2e/linux.max/dependencies.bicep | 8 +- .../tests/e2e/linux.max/main.test.bicep | 11 +- .../tests/e2e/waf-aligned/dependencies.bicep | 8 +- .../tests/e2e/waf-aligned/main.test.bicep | 8 +- .../e2e/windows.defaults/main.test.bicep | 2 +- .../main.test.bicep | 2 +- .../e2e/windows.hostpool/main.test.bicep | 12 +- .../tests/e2e/windows.max/dependencies.bicep | 8 +- .../tests/e2e/windows.max/main.test.bicep | 13 +- .../tests/e2e/windows.nvidia/main.test.bicep | 8 +- .../tests/e2e/windows.ssecmk/main.test.bicep | 4 +- 17 files changed, 686 insertions(+), 157 deletions(-) diff --git a/avm/res/compute/virtual-machine/README.md b/avm/res/compute/virtual-machine/README.md index cdd5b09b49..9907ba0feb 100644 --- a/avm/res/compute/virtual-machine/README.md +++ b/avm/res/compute/virtual-machine/README.md @@ -89,7 +89,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -163,7 +163,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "osDisk": { "value": { - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -239,7 +239,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -308,7 +308,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -439,12 +439,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadOnly' - createOption: 'fromImage' + createOption: 'FromImage' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } + name: 'osdisk01' } osType: 'Linux' vmSize: 'Standard_DS2_v2' @@ -458,19 +459,21 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { caching: 'ReadWrite' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } + name: 'datadisk01' } { caching: 'ReadWrite' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } + name: 'datadisk02' } ] disablePasswordAuthentication: true @@ -703,12 +706,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadOnly", - "createOption": "fromImage", + "createOption": "FromImage", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" - } + }, + "name": "osdisk01" } }, "osType": { @@ -736,19 +740,21 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "caching": "ReadWrite", "createOption": "Empty", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" - } + }, + "name": "datadisk01" }, { "caching": "ReadWrite", "createOption": "Empty", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" - } + }, + "name": "datadisk02" } ] }, @@ -1011,9 +1017,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - createOption: 'fromImage' + createOption: 'FromImage' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -1031,7 +1037,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { caching: 'ReadOnly' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -1040,7 +1046,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { caching: 'ReadOnly' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -1292,9 +1298,9 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "createOption": "fromImage", + "createOption": "FromImage", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -1328,7 +1334,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "caching": "ReadOnly", "createOption": "Empty", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -1337,7 +1343,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "caching": "ReadOnly", "createOption": "Empty", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -1558,7 +1564,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -1618,7 +1624,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -1680,7 +1686,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -1769,7 +1775,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -1866,7 +1872,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -1949,7 +1955,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -2101,12 +2107,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - createOption: 'fromImage' + createOption: 'FromImage' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } + name: 'osdisk01' } osType: 'Windows' vmSize: 'Standard_DS2_v2' @@ -2121,19 +2128,23 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { caching: 'None' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 + lun: 0 managedDisk: { storageAccountType: 'Premium_LRS' } + name: 'datadisk01' } { caching: 'None' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 + lun: 1 managedDisk: { storageAccountType: 'Premium_LRS' } + name: 'datadisk02' } ] enableAutomaticUpdates: true @@ -2383,12 +2394,13 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "createOption": "fromImage", + "createOption": "FromImage", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" - } + }, + "name": "osdisk01" } }, "osType": { @@ -2419,19 +2431,23 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "caching": "None", "createOption": "Empty", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, + "lun": 0, "managedDisk": { "storageAccountType": "Premium_LRS" - } + }, + "name": "datadisk01" }, { "caching": "None", "createOption": "Empty", "deleteOption": "Delete", - "diskSizeGB": "128", + "diskSizeGB": 128, + "lun": 1, "managedDisk": { "storageAccountType": "Premium_LRS" - } + }, + "name": "datadisk02" } ] }, @@ -2650,7 +2666,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { ] osDisk: { caching: 'ReadWrite' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -2713,7 +2729,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "osDisk": { "value": { "caching": "ReadWrite", - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "storageAccountType": "Premium_LRS" } @@ -2779,7 +2795,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { diskEncryptionSet: { id: '' @@ -2793,7 +2809,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { adminPassword: '' dataDisks: [ { - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { diskEncryptionSet: { id: '' @@ -2852,7 +2868,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { }, "osDisk": { "value": { - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "diskEncryptionSet": { "id": "" @@ -2874,7 +2890,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:' = { "dataDisks": { "value": [ { - "diskSizeGB": "128", + "diskSizeGB": 128, "managedDisk": { "diskEncryptionSet": { "id": "" @@ -3046,6 +3062,125 @@ Specifies the OS disk. For security reasons, it is recommended to specify DiskEn - Required: Yes - Type: object +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`diskSizeGB`](#parameter-osdiskdisksizegb) | int | Specifies the size of an empty data disk in gigabytes. | +| [`managedDisk`](#parameter-osdiskmanageddisk) | object | The managed disk parameters. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`caching`](#parameter-osdiskcaching) | string | Specifies the caching requirements. | +| [`createOption`](#parameter-osdiskcreateoption) | string | Specifies how the virtual machine should be created. | +| [`deleteOption`](#parameter-osdiskdeleteoption) | string | Specifies whether data disk should be deleted or detached upon VM deletion. | +| [`name`](#parameter-osdiskname) | string | The disk name. | + +### Parameter: `osDisk.diskSizeGB` + +Specifies the size of an empty data disk in gigabytes. + +- Required: Yes +- Type: int + +### Parameter: `osDisk.managedDisk` + +The managed disk parameters. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`storageAccountType`](#parameter-osdiskmanageddiskstorageaccounttype) | string | Specifies the storage account type for the managed disk. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`diskEncryptionSetResourceId`](#parameter-osdiskmanageddiskdiskencryptionsetresourceid) | string | Specifies the customer managed disk encryption set resource id for the managed disk. | + +### Parameter: `osDisk.managedDisk.storageAccountType` + +Specifies the storage account type for the managed disk. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Premium_LRS' + 'Premium_ZRS' + 'PremiumV2_LRS' + 'Standard_LRS' + 'StandardSSD_LRS' + 'StandardSSD_ZRS' + 'UltraSSD_LRS' + ] + ``` + +### Parameter: `osDisk.managedDisk.diskEncryptionSetResourceId` + +Specifies the customer managed disk encryption set resource id for the managed disk. + +- Required: No +- Type: string + +### Parameter: `osDisk.caching` + +Specifies the caching requirements. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'None' + 'ReadOnly' + 'ReadWrite' + ] + ``` + +### Parameter: `osDisk.createOption` + +Specifies how the virtual machine should be created. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Attach' + 'Empty' + 'FromImage' + ] + ``` + +### Parameter: `osDisk.deleteOption` + +Specifies whether data disk should be deleted or detached upon VM deletion. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Delete' + 'Detach' + ] + ``` + +### Parameter: `osDisk.name` + +The disk name. + +- Required: No +- Type: string + ### Parameter: `osType` The chosen OS type. @@ -3177,7 +3312,133 @@ Specifies the data disks. For security reasons, it is recommended to specify Dis - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`diskSizeGB`](#parameter-datadisksdisksizegb) | int | Specifies the size of an empty data disk in gigabytes. | +| [`managedDisk`](#parameter-datadisksmanageddisk) | object | The managed disk parameters. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`caching`](#parameter-datadiskscaching) | string | Specifies the caching requirements. | +| [`createOption`](#parameter-datadiskscreateoption) | string | Specifies how the virtual machine should be created. | +| [`deleteOption`](#parameter-datadisksdeleteoption) | string | Specifies whether data disk should be deleted or detached upon VM deletion. | +| [`lun`](#parameter-datadiskslun) | int | Specifies the logical unit number of the data disk. | +| [`name`](#parameter-datadisksname) | string | The disk name. | + +### Parameter: `dataDisks.diskSizeGB` + +Specifies the size of an empty data disk in gigabytes. + +- Required: Yes +- Type: int + +### Parameter: `dataDisks.managedDisk` + +The managed disk parameters. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`storageAccountType`](#parameter-datadisksmanageddiskstorageaccounttype) | string | Specifies the storage account type for the managed disk. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`diskEncryptionSetResourceId`](#parameter-datadisksmanageddiskdiskencryptionsetresourceid) | string | Specifies the customer managed disk encryption set resource id for the managed disk. | + +### Parameter: `dataDisks.managedDisk.storageAccountType` + +Specifies the storage account type for the managed disk. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Premium_LRS' + 'Premium_ZRS' + 'PremiumV2_LRS' + 'Standard_LRS' + 'StandardSSD_LRS' + 'StandardSSD_ZRS' + 'UltraSSD_LRS' + ] + ``` + +### Parameter: `dataDisks.managedDisk.diskEncryptionSetResourceId` + +Specifies the customer managed disk encryption set resource id for the managed disk. + +- Required: No +- Type: string + +### Parameter: `dataDisks.caching` + +Specifies the caching requirements. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'None' + 'ReadOnly' + 'ReadWrite' + ] + ``` + +### Parameter: `dataDisks.createOption` + +Specifies how the virtual machine should be created. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Attach' + 'Empty' + 'FromImage' + ] + ``` + +### Parameter: `dataDisks.deleteOption` + +Specifies whether data disk should be deleted or detached upon VM deletion. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Delete' + 'Detach' + ] + ``` + +### Parameter: `dataDisks.lun` + +Specifies the logical unit number of the data disk. + +- Required: No +- Type: int + +### Parameter: `dataDisks.name` + +The disk name. + +- Required: No +- Type: string ### Parameter: `dedicatedHostId` diff --git a/avm/res/compute/virtual-machine/extension/main.json b/avm/res/compute/virtual-machine/extension/main.json index 73e9c680a6..230becd81b 100644 --- a/avm/res/compute/virtual-machine/extension/main.json +++ b/avm/res/compute/virtual-machine/extension/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.25.53.49325", - "templateHash": "5949079428725139834" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", diff --git a/avm/res/compute/virtual-machine/main.bicep b/avm/res/compute/virtual-machine/main.bicep index 1fe7b22def..4d5e48280a 100644 --- a/avm/res/compute/virtual-machine/main.bicep +++ b/avm/res/compute/virtual-machine/main.bicep @@ -30,10 +30,10 @@ param imageReference object param plan object = {} @description('Required. Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs.') -param osDisk object +param osDisk osDiskType @description('Optional. Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs.') -param dataDisks array = [] +param dataDisks dataDisksType @description('Optional. The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled.') param ultraSSDEnabled bool = false @@ -474,35 +474,29 @@ resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = { storageProfile: { imageReference: imageReference osDisk: { - name: '${name}-disk-os-01' - createOption: contains(osDisk, 'createOption') ? osDisk.createOption : 'FromImage' - deleteOption: contains(osDisk, 'deleteOption') ? osDisk.deleteOption : 'Delete' + name: osDisk.?name ?? '${name}-disk-os-01' + createOption: osDisk.?createOption ?? 'FromImage' + deleteOption: osDisk.?deleteOption ?? 'Delete' diskSizeGB: osDisk.diskSizeGB - caching: contains(osDisk, 'caching') ? osDisk.caching : 'ReadOnly' + caching: osDisk.?caching ?? 'ReadOnly' managedDisk: { storageAccountType: osDisk.managedDisk.storageAccountType - diskEncryptionSet: contains(osDisk.managedDisk, 'diskEncryptionSet') - ? { - id: osDisk.managedDisk.diskEncryptionSet.id - } - : null + diskEncryptionSet: { + id: osDisk.managedDisk.?diskEncryptionSetResourceId + } } } dataDisks: [ - for (dataDisk, index) in dataDisks: { - lun: index - name: '${name}-disk-data-${padLeft((index + 1), 2, '0')}' + for (dataDisk, index) in dataDisks ?? []: { + lun: dataDisk.?lun ?? index + name: dataDisk.?name ?? '${name}-disk-data-${padLeft((index + 1), 2, '0')}' diskSizeGB: dataDisk.diskSizeGB - createOption: contains(dataDisk, 'createOption') ? dataDisk.createOption : 'Empty' - deleteOption: contains(dataDisk, 'deleteOption') ? dataDisk.deleteOption : 'Delete' - caching: contains(dataDisk, 'caching') ? dataDisk.caching : 'ReadOnly' + createOption: dataDisk.?createoption ?? 'Empty' + deleteOption: dataDisk.?deleteOption ?? 'Delete' + caching: dataDisk.?caching ?? 'ReadOnly' managedDisk: { storageAccountType: dataDisk.managedDisk.storageAccountType - diskEncryptionSet: contains(dataDisk.managedDisk, 'diskEncryptionSet') - ? { - id: dataDisk.managedDisk.diskEncryptionSet.id - } - : null + diskEncryptionSet: dataDisk.?managedDisk.?diskEncryptionSet ?? null } } ] @@ -552,6 +546,7 @@ resource vm 'Microsoft.Compute/virtualMachines@2022-11-01' = { : null priority: priority evictionPolicy: enableEvictionPolicy ? 'Deallocate' : null + #disable-next-line BCP036 billingProfile: !empty(priority) && !empty(maxPriceForLowPriorityVm) ? { maxPrice: json(maxPriceForLowPriorityVm) @@ -1060,3 +1055,74 @@ type roleAssignmentType = { @description('Optional. The Resource Id of the delegated managed identity resource.') delegatedManagedIdentityResourceId: string? }[]? + +type osDiskType = { + @description('Optional. The disk name.') + name: string? + + @description('Required. Specifies the size of an empty data disk in gigabytes.') + @maxValue(1023) + diskSizeGB: int + + @description('Optional. Specifies how the virtual machine should be created.') + createOption: 'Attach' | 'Empty' | 'FromImage'? + + @description('Optional. Specifies whether data disk should be deleted or detached upon VM deletion.') + deleteOption: 'Delete' | 'Detach'? + + @description('Optional. Specifies the caching requirements.') + caching: 'None' | 'ReadOnly' | 'ReadWrite'? + + @description('Required. The managed disk parameters.') + managedDisk: { + @description('Required. Specifies the storage account type for the managed disk.') + storageAccountType: + | 'PremiumV2_LRS' + | 'Premium_LRS' + | 'Premium_ZRS' + | 'StandardSSD_LRS' + | 'StandardSSD_ZRS' + | 'Standard_LRS' + | 'UltraSSD_LRS' + + @description('Optional. Specifies the customer managed disk encryption set resource id for the managed disk.') + diskEncryptionSetResourceId: string? + } +} + +type dataDisksType = { + @description('Optional. The disk name.') + name: string? + + @description('Optional. Specifies the logical unit number of the data disk.') + lun: int? + + @description('Required. Specifies the size of an empty data disk in gigabytes.') + @maxValue(1023) + diskSizeGB: int + + @description('Optional. Specifies how the virtual machine should be created.') + createOption: 'Attach' | 'Empty' | 'FromImage'? + + @description('Optional. Specifies whether data disk should be deleted or detached upon VM deletion.') + deleteOption: 'Delete' | 'Detach'? + + @description('Optional. Specifies the caching requirements.') + caching: 'None' | 'ReadOnly' | 'ReadWrite'? + + @description('Required. The managed disk parameters.') + managedDisk: { + @description('Required. Specifies the storage account type for the managed disk.') + storageAccountType: + | 'PremiumV2_LRS' + | 'Premium_LRS' + | 'Premium_ZRS' + | 'StandardSSD_LRS' + | 'StandardSSD_ZRS' + | 'Standard_LRS' + | 'UltraSSD_LRS' + + @description('Optional. Specifies the customer managed disk encryption set resource id for the managed disk.') + diskEncryptionSetResourceId: string? + } +}[]? diff --git a/avm/res/compute/virtual-machine/main.json b/avm/res/compute/virtual-machine/main.json index 5d9c60bd68..0d19a77932 100644 --- a/avm/res/compute/virtual-machine/main.json +++ b/avm/res/compute/virtual-machine/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5410471245439722622" + "version": "0.26.170.59819", + "templateHash": "15754009504409627537" }, "name": "Virtual Machines", "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs.", @@ -126,6 +126,185 @@ } }, "nullable": true + }, + "osDiskType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "diskSizeGB": { + "type": "int", + "maxValue": 1023, + "metadata": { + "description": "Required. Specifies the size of an empty data disk in gigabytes." + } + }, + "createOption": { + "type": "string", + "allowedValues": [ + "Attach", + "Empty", + "FromImage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies how the virtual machine should be created." + } + }, + "deleteOption": { + "type": "string", + "allowedValues": [ + "Delete", + "Detach" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether data disk should be deleted or detached upon VM deletion." + } + }, + "caching": { + "type": "string", + "allowedValues": [ + "None", + "ReadOnly", + "ReadWrite" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the caching requirements." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. Specifies the storage account type for the managed disk." + } + }, + "diskEncryptionSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the customer managed disk encryption set resource id for the managed disk." + } + } + }, + "metadata": { + "description": "Required. The managed disk parameters." + } + } + } + }, + "dataDisksType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "lun": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the logical unit number of the data disk." + } + }, + "diskSizeGB": { + "type": "int", + "maxValue": 1023, + "metadata": { + "description": "Required. Specifies the size of an empty data disk in gigabytes." + } + }, + "createOption": { + "type": "string", + "allowedValues": [ + "Attach", + "Empty", + "FromImage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies how the virtual machine should be created." + } + }, + "deleteOption": { + "type": "string", + "allowedValues": [ + "Delete", + "Detach" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether data disk should be deleted or detached upon VM deletion." + } + }, + "caching": { + "type": "string", + "allowedValues": [ + "None", + "ReadOnly", + "ReadWrite" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the caching requirements." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "metadata": { + "description": "Required. Specifies the storage account type for the managed disk." + } + }, + "diskEncryptionSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the customer managed disk encryption set resource id for the managed disk." + } + } + }, + "metadata": { + "description": "Required. The managed disk parameters." + } + } + } + }, + "nullable": true } }, "parameters": { @@ -190,14 +369,13 @@ } }, "osDisk": { - "type": "object", + "$ref": "#/definitions/osDiskType", "metadata": { "description": "Required. Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs." } }, "dataDisks": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/dataDisksType", "metadata": { "description": "Optional. Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs." } @@ -749,31 +927,33 @@ "copy": [ { "name": "dataDisks", - "count": "[length(parameters('dataDisks'))]", + "count": "[length(coalesce(parameters('dataDisks'), createArray()))]", "input": { - "lun": "[copyIndex('dataDisks')]", - "name": "[format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0'))]", - "diskSizeGB": "[parameters('dataDisks')[copyIndex('dataDisks')].diskSizeGB]", - "createOption": "[if(contains(parameters('dataDisks')[copyIndex('dataDisks')], 'createOption'), parameters('dataDisks')[copyIndex('dataDisks')].createOption, 'Empty')]", - "deleteOption": "[if(contains(parameters('dataDisks')[copyIndex('dataDisks')], 'deleteOption'), parameters('dataDisks')[copyIndex('dataDisks')].deleteOption, 'Delete')]", - "caching": "[if(contains(parameters('dataDisks')[copyIndex('dataDisks')], 'caching'), parameters('dataDisks')[copyIndex('dataDisks')].caching, 'ReadOnly')]", + "lun": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'lun'), copyIndex('dataDisks'))]", + "name": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))]", + "diskSizeGB": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].diskSizeGB]", + "createOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'createoption'), 'Empty')]", + "deleteOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'deleteOption'), 'Delete')]", + "caching": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'caching'), 'ReadOnly')]", "managedDisk": { - "storageAccountType": "[parameters('dataDisks')[copyIndex('dataDisks')].managedDisk.storageAccountType]", - "diskEncryptionSet": "[if(contains(parameters('dataDisks')[copyIndex('dataDisks')].managedDisk, 'diskEncryptionSet'), createObject('id', parameters('dataDisks')[copyIndex('dataDisks')].managedDisk.diskEncryptionSet.id), null())]" + "storageAccountType": "[coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.storageAccountType]", + "diskEncryptionSet": "[coalesce(tryGet(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'managedDisk'), 'diskEncryptionSet'), null())]" } } } ], "imageReference": "[parameters('imageReference')]", "osDisk": { - "name": "[format('{0}-disk-os-01', parameters('name'))]", - "createOption": "[if(contains(parameters('osDisk'), 'createOption'), parameters('osDisk').createOption, 'FromImage')]", - "deleteOption": "[if(contains(parameters('osDisk'), 'deleteOption'), parameters('osDisk').deleteOption, 'Delete')]", + "name": "[coalesce(tryGet(parameters('osDisk'), 'name'), format('{0}-disk-os-01', parameters('name')))]", + "createOption": "[coalesce(tryGet(parameters('osDisk'), 'createOption'), 'FromImage')]", + "deleteOption": "[coalesce(tryGet(parameters('osDisk'), 'deleteOption'), 'Delete')]", "diskSizeGB": "[parameters('osDisk').diskSizeGB]", - "caching": "[if(contains(parameters('osDisk'), 'caching'), parameters('osDisk').caching, 'ReadOnly')]", + "caching": "[coalesce(tryGet(parameters('osDisk'), 'caching'), 'ReadOnly')]", "managedDisk": { "storageAccountType": "[parameters('osDisk').managedDisk.storageAccountType]", - "diskEncryptionSet": "[if(contains(parameters('osDisk').managedDisk, 'diskEncryptionSet'), createObject('id', parameters('osDisk').managedDisk.diskEncryptionSet.id), null())]" + "diskEncryptionSet": { + "id": "[tryGet(parameters('osDisk').managedDisk, 'diskEncryptionSetResourceId')]" + } } } }, @@ -949,8 +1129,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13415879962041783892" + "version": "0.26.170.59819", + "templateHash": "7272408028164257685" } }, "definitions": { @@ -2419,8 +2599,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -2625,8 +2805,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -2827,8 +3007,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3041,8 +3221,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3239,8 +3419,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3436,8 +3616,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3637,8 +3817,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -3846,8 +4026,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4047,8 +4227,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4246,8 +4426,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4456,8 +4636,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4656,8 +4836,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16251967456691023698" + "version": "0.26.170.59819", + "templateHash": "8063436714999902780" }, "name": "Virtual Machine Extensions", "description": "This module deploys a Virtual Machine Extension.", @@ -4855,8 +5035,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "5385249890312845255" + "version": "0.26.170.59819", + "templateHash": "2393851439891433510" }, "name": "Recovery Service Vaults Protection Container Protected Item", "description": "This module deploys a Recovery Services Vault Protection Container Protected Item.", diff --git a/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep index 419c52c46d..47e5049f6d 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/atmg/main.test.bicep @@ -20,6 +20,10 @@ param serviceShort string = 'cvmlinatmg' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// Set to fixed location as the RP function returns unsupported locations (configurationProfileAssignments) +// Right now (2024/04) the following locations are supported: centralus, eastus, eastus2, southcentralus, westus, westus2, westcentralus, northeurope, westeurope, canadacentral, japaneast, uksouth, australiasoutheast, australiaeast, southeastasia, westus3 +param enforcedLocation string = 'westeurope' + // ============ // // Dependencies // // ============ // @@ -33,9 +37,9 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { - location: resourceLocation + location: enforcedLocation virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' sshDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' sshKeyName: 'dep-${namePrefix}-ssh-${serviceShort}' @@ -56,9 +60,9 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { - location: resourceLocation + location: enforcedLocation name: '${namePrefix}${serviceShort}' adminUsername: 'localAdminUser' imageReference: { @@ -88,7 +92,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep index f1547f9b26..6f646b2f4b 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.defaults/main.test.bicep @@ -83,7 +83,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 caching: 'ReadWrite' managedDisk: { storageAccountType: 'Premium_LRS' diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep index b45e84c615..594807efaf 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/dependencies.bicep @@ -88,7 +88,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = { { name: 'privateIPConfig1' properties: { - subnet: virtualNetwork.properties.subnets[0] + subnet: { + id: virtualNetwork.properties.subnets[0].id + } } } ] @@ -218,10 +220,10 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } resource backupServiceKeyVaultPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${keyVault.name}-${location}-268f6a53-9f68-4a38-ae47-166f730d86af-KeyVault-KeyVaultAdministrator-RoleAssignment') + name: guid('${keyVault.name}-${location}-94d1f9d9-9caf-4e06-809e-29fc95f69e65-KeyVault-KeyVaultAdministrator-RoleAssignment') scope: keyVault properties: { - principalId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) + principalId: '94d1f9d9-9caf-4e06-809e-29fc95f69e65' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483' diff --git a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep index 5bc3497300..4fa2f77428 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/linux.max/main.test.bicep @@ -154,10 +154,11 @@ module testDeployment '../../../main.bicep' = { } ] osDisk: { + name: 'osdisk01' caching: 'ReadOnly' - createOption: 'fromImage' + createOption: 'FromImage' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -170,19 +171,21 @@ module testDeployment '../../../main.bicep' = { backupVaultResourceGroup: nestedDependencies.outputs.recoveryServicesVaultResourceGroupName dataDisks: [ { + name: 'datadisk01' caching: 'ReadWrite' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } } { + name: 'datadisk02' caching: 'ReadWrite' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep index 8e1705682c..f9979cbc71 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/dependencies.bicep @@ -85,7 +85,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = { { name: 'privateIPConfig1' properties: { - subnet: virtualNetwork.properties.subnets[0] + subnet: { + id: virtualNetwork.properties.subnets[0].id + } } } ] @@ -215,10 +217,10 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } resource backupServiceKeyVaultPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${keyVault.name}-${location}-268f6a53-9f68-4a38-ae47-166f730d86af-KeyVault-KeyVaultAdministrator-RoleAssignment') + name: guid('${keyVault.name}-${location}-94d1f9d9-9caf-4e06-809e-29fc95f69e65-KeyVault-KeyVaultAdministrator-RoleAssignment') scope: keyVault properties: { - principalId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) + principalId: '94d1f9d9-9caf-4e06-809e-29fc95f69e65' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483' diff --git a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep index 76cd0f2393..4090a9830a 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/waf-aligned/main.test.bicep @@ -160,9 +160,9 @@ module testDeployment '../../../main.bicep' = [ ] osDisk: { caching: 'ReadWrite' - createOption: 'fromImage' + createOption: 'FromImage' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -179,7 +179,7 @@ module testDeployment '../../../main.bicep' = [ caching: 'ReadOnly' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -188,7 +188,7 @@ module testDeployment '../../../main.bicep' = [ caching: 'ReadOnly' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep index 0322bf0a51..5e6f6365dd 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.defaults/main.test.bicep @@ -75,7 +75,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 caching: 'ReadWrite' managedDisk: { storageAccountType: 'Premium_LRS' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep index 36db7d031d..c676412329 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.guestconfiguration/main.test.bicep @@ -78,7 +78,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 caching: 'ReadWrite' managedDisk: { storageAccountType: 'Premium_LRS' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep index 5a55be165f..1c198d0c69 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.hostpool/main.test.bicep @@ -24,6 +24,10 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// Set to fixed location as the RP function returns unsupported locations +// Right now (2024/04) the following locations are supported: centralindia,uksouth,ukwest,japaneast,australiaeast,canadaeast,canadacentral,northeurope,westeurope,eastus,eastus2,westus,westus2,westus3,northcentralus,southcentralus,westcentralus,centralus +param enforcedLocation string = 'westeurope' + // ============ // // Dependencies // // ============ // @@ -37,9 +41,9 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { - location: resourceLocation + location: enforcedLocation virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' hostPoolName: 'dep${namePrefix}-hp-${serviceShort}01' @@ -56,7 +60,7 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - location: resourceLocation + location: enforcedLocation name: '${namePrefix}${serviceShort}' adminUsername: 'localAdminUser' managedIdentities: { @@ -81,7 +85,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 caching: 'ReadWrite' managedDisk: { storageAccountType: 'Premium_LRS' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep index 8e1705682c..f9979cbc71 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/dependencies.bicep @@ -85,7 +85,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2023-04-01' = { { name: 'privateIPConfig1' properties: { - subnet: virtualNetwork.properties.subnets[0] + subnet: { + id: virtualNetwork.properties.subnets[0].id + } } } ] @@ -215,10 +217,10 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { } resource backupServiceKeyVaultPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${keyVault.name}-${location}-268f6a53-9f68-4a38-ae47-166f730d86af-KeyVault-KeyVaultAdministrator-RoleAssignment') + name: guid('${keyVault.name}-${location}-94d1f9d9-9caf-4e06-809e-29fc95f69e65-KeyVault-KeyVaultAdministrator-RoleAssignment') scope: keyVault properties: { - principalId: '268f6a53-9f68-4a38-ae47-166f730d86af' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) + principalId: '94d1f9d9-9caf-4e06-809e-29fc95f69e65' // Backup Management Service Enterprise Application Object Id (Note: this is tenant specific) roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep index 01c03b31dd..633d6116f2 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.max/main.test.bicep @@ -159,10 +159,11 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { + name: 'osdisk01' caching: 'ReadWrite' - createOption: 'fromImage' + createOption: 'FromImage' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } @@ -176,19 +177,23 @@ module testDeployment '../../../main.bicep' = [ backupVaultResourceGroup: nestedDependencies.outputs.recoveryServicesVaultResourceGroupName dataDisks: [ { + name: 'datadisk01' + lun: 0 caching: 'None' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } } { + name: 'datadisk02' + lun: 1 caching: 'None' createOption: 'Empty' deleteOption: 'Delete' - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' } diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep index 40778baf54..603cf8c4fc 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.nvidia/main.test.bicep @@ -12,7 +12,7 @@ metadata description = 'This instance deploys the module for a VM with dedicated param resourceGroupName string = 'dep-${namePrefix}-compute.virtualMachines-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +param resourceLocation string = 'eastus' @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'cvmwinnvidia' @@ -24,7 +24,7 @@ param password string = newGuid() @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' -#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be ued in the AVM testing subscription +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription var tempLocation = 'eastus' // ============ // @@ -35,7 +35,7 @@ var tempLocation = 'eastus' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: tempLocation + location: resourceLocation } module nestedDependencies 'dependencies.bicep' = { @@ -78,7 +78,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 caching: 'ReadWrite' managedDisk: { storageAccountType: 'Premium_LRS' diff --git a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep index e0d0b19cbc..cffd564ce1 100644 --- a/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep +++ b/avm/res/compute/virtual-machine/tests/e2e/windows.ssecmk/main.test.bicep @@ -81,7 +81,7 @@ module testDeployment '../../../main.bicep' = [ } ] osDisk: { - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' diskEncryptionSet: { @@ -94,7 +94,7 @@ module testDeployment '../../../main.bicep' = [ adminPassword: password dataDisks: [ { - diskSizeGB: '128' + diskSizeGB: 128 managedDisk: { storageAccountType: 'Premium_LRS' diskEncryptionSet: { From 46d965bc80e86e13c5c39aef9a204a308ce62e44 Mon Sep 17 00:00:00 2001 From: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Date: Mon, 15 Apr 2024 17:37:20 +0200 Subject: [PATCH 40/66] fix: pwsh publish module index (#1683) ## Description Removing an unnecessary `-Force` and unrequired `SupportsShouldProcess` from the `Invoke-AvmJsonModuleIndexGeneration.ps1` ## Type of Change - [x] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/workflows/avm.platform.publish-module-index-json.yml | 2 +- .../pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/avm.platform.publish-module-index-json.yml b/.github/workflows/avm.platform.publish-module-index-json.yml index d82f31317e..a8f2076487 100644 --- a/.github/workflows/avm.platform.publish-module-index-json.yml +++ b/.github/workflows/avm.platform.publish-module-index-json.yml @@ -51,7 +51,7 @@ jobs: Write-Verbose "Invoke task with" -Verbose Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose - if(-not (Invoke-AvmJsonModuleIndexGeneration @functionInput -Force)) { + if(-not (Invoke-AvmJsonModuleIndexGeneration @functionInput)) { Write-Output ('{0}={1}' -f 'anyErrorsOccurred', $true) >> $env:GITHUB_ENV } diff --git a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 index 49a58895c5..c96eb49f15 100644 --- a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 +++ b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 @@ -40,7 +40,6 @@ The function requires Azure PowerShell Storage Module (Az.Storage) to be install #> function Invoke-AvmJsonModuleIndexGeneration { - [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $false)] [string] $storageAccountName = 'biceplivedatasaprod', From 025b862962f5b1d49519aaf34f85f1f0a087b052 Mon Sep 17 00:00:00 2001 From: Kris Baranek Date: Wed, 17 Apr 2024 09:20:59 +0200 Subject: [PATCH 41/66] fix: WAF reliability and static validation in `avm/res/web/site` (#1427) ## Description - added PSRule exceptions for `defaults` tests - removed optional parameters from `defaults` tests - re-generated readme due to static validation errors fixes #1378 fixes #1510 fixes #1592 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.site](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=users%2Fkrbar%2FwebSiteFix)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.web.site.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/web/site/README.md | 24 +++++-------------- avm/res/web/site/main.bicep | 4 +++- avm/res/web/site/main.json | 12 ++++++---- avm/res/web/site/slot/README.md | 6 +++++ avm/res/web/site/slot/main.bicep | 4 +++- avm/res/web/site/slot/main.json | 6 +++-- .../e2e/functionApp.defaults/main.test.bicep | 3 --- .../tests/e2e/webApp.defaults/main.test.bicep | 4 ---- .../psrule/.ps-rule/min-suppress.Rule.yaml | 3 +++ 9 files changed, 33 insertions(+), 33 deletions(-) diff --git a/avm/res/web/site/README.md b/avm/res/web/site/README.md index a1d528472c..fd8403c4f3 100644 --- a/avm/res/web/site/README.md +++ b/avm/res/web/site/README.md @@ -65,9 +65,6 @@ module site 'br/public:avm/res/web/site:' = { serverFarmResourceId: '' // Non-required parameters location: '' - siteConfig: { - alwaysOn: true - } } } ``` @@ -97,11 +94,6 @@ module site 'br/public:avm/res/web/site:' = { // Non-required parameters "location": { "value": "" - }, - "siteConfig": { - "value": { - "alwaysOn": true - } } } } @@ -659,10 +651,6 @@ module site 'br/public:avm/res/web/site:' = { serverFarmResourceId: '' // Non-required parameters location: '' - siteConfig: { - alwaysOn: true - healthCheckPath: '/healthz' - } } } ``` @@ -692,12 +680,6 @@ module site 'br/public:avm/res/web/site:' = { // Non-required parameters "location": { "value": "" - }, - "siteConfig": { - "value": { - "alwaysOn": true, - "healthCheckPath": "/healthz" - } } } } @@ -2576,6 +2558,12 @@ The site config object. - Required: No - Type: object +- Default: + ```Bicep + { + alwaysOn: true + } + ``` ### Parameter: `slots` diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index c0571f9765..19fa05ed2d 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -56,7 +56,9 @@ param vnetRouteAllEnabled bool = false param scmSiteAlsoStopped bool = false @description('Optional. The site config object.') -param siteConfig object? +param siteConfig object = { + alwaysOn: true +} @description('Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions.') param storageAccountResourceId string? diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index c536503a67..3b31a486aa 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "6312828584864471342" + "templateHash": "14301920664802681394" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -543,7 +543,9 @@ }, "siteConfig": { "type": "object", - "nullable": true, + "defaultValue": { + "alwaysOn": true + }, "metadata": { "description": "Optional. The site config object." } @@ -1299,7 +1301,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "12157473410186645235" + "templateHash": "7436278786647572492" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -1815,7 +1817,9 @@ }, "siteConfig": { "type": "object", - "nullable": true, + "defaultValue": { + "alwaysOn": true + }, "metadata": { "description": "Optional. The site config object." } diff --git a/avm/res/web/site/slot/README.md b/avm/res/web/site/slot/README.md index 0ee42e70e8..d6bd88486f 100644 --- a/avm/res/web/site/slot/README.md +++ b/avm/res/web/site/slot/README.md @@ -949,6 +949,12 @@ The site config object. - Required: No - Type: object +- Default: + ```Bicep + { + alwaysOn: true + } + ``` ### Parameter: `storageAccountRequired` diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index 209954c101..f5aa6cf5f1 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -47,7 +47,9 @@ param storageAccountRequired bool = false param virtualNetworkSubnetId string? @description('Optional. The site config object.') -param siteConfig object? +param siteConfig object = { + alwaysOn: true +} @description('Optional. Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions.') param storageAccountResourceId string? diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index 959db7cbac..ca03c4ee64 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "12157473410186645235" + "templateHash": "7436278786647572492" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -522,7 +522,9 @@ }, "siteConfig": { "type": "object", - "nullable": true, + "defaultValue": { + "alwaysOn": true + }, "metadata": { "description": "Optional. The site config object." } diff --git a/avm/res/web/site/tests/e2e/functionApp.defaults/main.test.bicep b/avm/res/web/site/tests/e2e/functionApp.defaults/main.test.bicep index 49f241e3f0..9925860e2e 100644 --- a/avm/res/web/site/tests/e2e/functionApp.defaults/main.test.bicep +++ b/avm/res/web/site/tests/e2e/functionApp.defaults/main.test.bicep @@ -54,9 +54,6 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation kind: 'functionapp' serverFarmResourceId: nestedDependencies.outputs.serverFarmResourceId - siteConfig: { - alwaysOn: true - } } dependsOn: [ nestedDependencies diff --git a/avm/res/web/site/tests/e2e/webApp.defaults/main.test.bicep b/avm/res/web/site/tests/e2e/webApp.defaults/main.test.bicep index c4b0bce825..63bac17835 100644 --- a/avm/res/web/site/tests/e2e/webApp.defaults/main.test.bicep +++ b/avm/res/web/site/tests/e2e/webApp.defaults/main.test.bicep @@ -54,10 +54,6 @@ module testDeployment '../../../main.bicep' = [ location: resourceLocation kind: 'app' serverFarmResourceId: nestedDependencies.outputs.serverFarmResourceId - siteConfig: { - healthCheckPath: '/healthz' - alwaysOn: true - } } dependsOn: [ diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml index f8bd41fc01..dbea639dbf 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/min-suppress.Rule.yaml @@ -28,6 +28,9 @@ spec: # Azure Virtual Machine - Azure.VM.AMA - Azure.VM.Standalone + # Azure App Service + - Azure.AppService.WebProbe # Supressed as the probe path is specific to the app + - Azure.AppService.WebProbePath # Supressed as the probe path is specific to the app if: name: "." contains: From 58deb215ed5508b5e87b6196fcdd1469c16ee871 Mon Sep 17 00:00:00 2001 From: Joans Sandbox <44612095+Kathedra@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:25:44 +0200 Subject: [PATCH 42/66] fix: prevent updating the workspace repository configuration whenever no value is provided, so an existing configuration is not overwritten. (#1680) prevent updating the workspace repository configuration whenever no value is provided, so an existing configuration is not overwritten. ## Description ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Alexander Sehr Co-authored-by: jzandijk@ilionx.com --- avm/res/synapse/workspace/README.md | 1 - avm/res/synapse/workspace/main.bicep | 2 +- avm/res/synapse/workspace/main.json | 18 +++++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index 62fbe376dd..80e5572b87 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -1590,7 +1590,6 @@ Git integration settings. - Required: No - Type: object -- Default: `{}` ## Outputs diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index 1e9c17624b..7546fecdd7 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -74,7 +74,7 @@ param sqlAdministratorLogin string param sqlAdministratorLoginPassword string = '' @description('Optional. Git integration settings.') -param workspaceRepositoryConfiguration object = {} +param workspaceRepositoryConfiguration object? @description('Optional. The managed identity definition for this resource.') param managedIdentities managedIdentitiesType diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index 89049892d8..27f50b2a73 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "456170449467900103" + "version": "0.25.3.34343", + "templateHash": "14051939023718187274" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -587,7 +587,7 @@ }, "workspaceRepositoryConfiguration": { "type": "object", - "defaultValue": {}, + "nullable": true, "metadata": { "description": "Optional. Git integration settings." } @@ -811,8 +811,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3692817517911366371" + "version": "0.25.3.34343", + "templateHash": "10907909212229865317" }, "name": "Synapse Workspace Integration Runtimes", "description": "This module deploys a Synapse Workspace Integration Runtime.", @@ -909,8 +909,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "11661011043333744116" + "version": "0.25.3.34343", + "templateHash": "6689684157194442067" } }, "parameters": { @@ -997,8 +997,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "4754954307876642999" + "version": "0.25.3.34343", + "templateHash": "13839822979779705807" }, "name": "Synapse Workspaces Keys", "description": "This module deploys a Synapse Workspaces Key.", From a616f649dc9db0124488076dc1d0de7ecbe36a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenjie=20Yu=EF=BC=88MSFT=EF=BC=89?= <81678720+zedy-wj@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:19:17 +0800 Subject: [PATCH 43/66] feat: Add the creation of 'Microsoft.CognitiveServices/accounts/deployments' in Cognitive Service Module - `avm/res/cognitive-services/account` (#1466) ## Description Fixes #1131 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.cognitive-services.account](https://github.com/zedy-wj/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=fix%2F1131)](https://github.com/zedy-wj/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings @jongio - for notification. --------- Co-authored-by: zedy Co-authored-by: Alexander Sehr --- avm/res/cognitive-services/account/README.md | 217 +++++++++++++++++- avm/res/cognitive-services/account/main.bicep | 47 ++++ avm/res/cognitive-services/account/main.json | 98 +++++++- .../e2e/ai-model-deployment/main.test.bicep | 63 +++++ 4 files changed, 412 insertions(+), 13 deletions(-) create mode 100644 avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep diff --git a/avm/res/cognitive-services/account/README.md b/avm/res/cognitive-services/account/README.md index 587b599762..820802cdb8 100644 --- a/avm/res/cognitive-services/account/README.md +++ b/avm/res/cognitive-services/account/README.md @@ -18,6 +18,7 @@ This module deploys a Cognitive Service. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.CognitiveServices/accounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts) | +| `Microsoft.CognitiveServices/accounts/deployments` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts/deployments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | @@ -30,14 +31,101 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/cognitive-services/account:`. -- [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [As Speech Service](#example-3-as-speech-service) -- [Using Customer-Managed-Keys with System-Assigned identity](#example-4-using-customer-managed-keys-with-system-assigned-identity) -- [Using Customer-Managed-Keys with User-Assigned identity](#example-5-using-customer-managed-keys-with-user-assigned-identity) -- [WAF-aligned](#example-6-waf-aligned) +- [Using `deployments` in parameter set](#example-1-using-deployments-in-parameter-set) +- [Using only defaults](#example-2-using-only-defaults) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [As Speech Service](#example-4-as-speech-service) +- [Using Customer-Managed-Keys with System-Assigned identity](#example-5-using-customer-managed-keys-with-system-assigned-identity) +- [Using Customer-Managed-Keys with User-Assigned identity](#example-6-using-customer-managed-keys-with-user-assigned-identity) +- [WAF-aligned](#example-7-waf-aligned) -### Example 1: _Using only defaults_ +### Example 1: _Using `deployments` in parameter set_ + +This instance deploys the module with the AI model deployment feature. + + +

+ +via Bicep module + +```bicep +module account 'br/public:avm/res/cognitive-services/account:' = { + name: 'accountDeployment' + params: { + // Required parameters + kind: 'AIServices' + name: 'csad002' + // Non-required parameters + customSubDomainName: 'xcsadai' + deployments: [ + { + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0301' + } + name: 'gpt-35-turbo' + sku: { + capacity: 10 + name: 'Standard' + } + } + ] + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "AIServices" + }, + "name": { + "value": "csad002" + }, + // Non-required parameters + "customSubDomainName": { + "value": "xcsadai" + }, + "deployments": { + "value": [ + { + "model": { + "format": "OpenAI", + "name": "gpt-35-turbo", + "version": "0301" + }, + "name": "gpt-35-turbo", + "sku": { + "capacity": 10, + "name": "Standard" + } + } + ] + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -89,7 +177,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 2: _Using large parameter set_ +### Example 3: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -377,7 +465,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 3: _As Speech Service_ +### Example 4: _As Speech Service_ This instance deploys the module as a Speech Service. @@ -491,7 +579,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 4: _Using Customer-Managed-Keys with System-Assigned identity_ +### Example 5: _Using Customer-Managed-Keys with System-Assigned identity_ This instance deploys the module using Customer-Managed-Keys using a System-Assigned Identity. This required the service to be deployed twice, once as a pre-requisite to create the System-Assigned Identity, and once to use it for accessing the Customer-Managed-Key secret. @@ -573,7 +661,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 5: _Using Customer-Managed-Keys with User-Assigned identity_ +### Example 6: _Using Customer-Managed-Keys with User-Assigned identity_ This instance deploys the module using Customer-Managed-Keys using a User-Assigned Identity to access the Customer-Managed-Key secret. @@ -661,7 +749,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 6: _WAF-aligned_ +### Example 7: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -820,6 +908,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = { | [`allowedFqdnList`](#parameter-allowedfqdnlist) | array | List of allowed FQDN. | | [`apiProperties`](#parameter-apiproperties) | object | The API properties for special APIs. | | [`customerManagedKey`](#parameter-customermanagedkey) | object | The customer managed key definition. | +| [`deployments`](#parameter-deployments) | array | Array of deployments about cognitive service accounts to create. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableLocalAuth`](#parameter-disablelocalauth) | bool | Allow only Azure AD authentication. Should be enabled for security reasons. | | [`dynamicThrottlingEnabled`](#parameter-dynamicthrottlingenabled) | bool | The flag to enable dynamic throttling. | @@ -956,6 +1045,110 @@ User assigned identity to use when fetching the customer managed key. Required i - Required: No - Type: string +### Parameter: `deployments` + +Array of deployments about cognitive service accounts to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`model`](#parameter-deploymentsmodel) | object | Properties of Cognitive Services account deployment model. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-deploymentsname) | string | Specify the name of cognitive service account deployment. | +| [`raiPolicyName`](#parameter-deploymentsraipolicyname) | string | The name of RAI policy. | +| [`sku`](#parameter-deploymentssku) | object | The resource model definition representing SKU. | + +### Parameter: `deployments.model` + +Properties of Cognitive Services account deployment model. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`format`](#parameter-deploymentsmodelformat) | string | The format of Cognitive Services account deployment model. | +| [`name`](#parameter-deploymentsmodelname) | string | The name of Cognitive Services account deployment model. | +| [`version`](#parameter-deploymentsmodelversion) | string | The version of Cognitive Services account deployment model. | + +### Parameter: `deployments.model.format` + +The format of Cognitive Services account deployment model. + +- Required: Yes +- Type: string + +### Parameter: `deployments.model.name` + +The name of Cognitive Services account deployment model. + +- Required: Yes +- Type: string + +### Parameter: `deployments.model.version` + +The version of Cognitive Services account deployment model. + +- Required: Yes +- Type: string + +### Parameter: `deployments.name` + +Specify the name of cognitive service account deployment. + +- Required: No +- Type: string + +### Parameter: `deployments.raiPolicyName` + +The name of RAI policy. + +- Required: No +- Type: string + +### Parameter: `deployments.sku` + +The resource model definition representing SKU. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-deploymentsskuname) | string | The name of the resource model definition representing SKU. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`capacity`](#parameter-deploymentsskucapacity) | int | The capacity of the resource model definition representing SKU. | + +### Parameter: `deployments.sku.name` + +The name of the resource model definition representing SKU. + +- Required: Yes +- Type: string + +### Parameter: `deployments.sku.capacity` + +The capacity of the resource model definition representing SKU. + +- Required: No +- Type: int + ### Parameter: `diagnosticSettings` The diagnostic settings of the service. diff --git a/avm/res/cognitive-services/account/main.bicep b/avm/res/cognitive-services/account/main.bicep index d88001d7f3..293821f9b8 100644 --- a/avm/res/cognitive-services/account/main.bicep +++ b/avm/res/cognitive-services/account/main.bicep @@ -126,6 +126,9 @@ param managedIdentities managedIdentitiesType @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true +@description('Optional. Array of deployments about cognitive service accounts to create.') +param deployments deploymentsType + var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -339,6 +342,21 @@ resource cognitiveService 'Microsoft.CognitiveServices/accounts@2023-05-01' = { } } +@batchSize(1) +resource cognitiveService_deployments 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [ + for (deployment, index) in (deployments ?? []): { + parent: cognitiveService + name: deployment.?name ?? '${name}-deployments' + properties: { + model: deployment.model + raiPolicyName: deployment.?raiPolicyName + } + sku: deployment.?sku ?? { + name: sku + } + } +] + resource cognitiveService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { name: lock.?name ?? 'lock-${name}' @@ -649,3 +667,32 @@ type lockType = { @description('Optional. Specify the type of lock.') kind: ('CanNotDelete' | 'ReadOnly' | 'None')? }? + +type deploymentsType = { + @description('Optional. Specify the name of cognitive service account deployment.') + name: string? + + @description('Required. Properties of Cognitive Services account deployment model.') + model: { + @description('Required. The name of Cognitive Services account deployment model.') + name: string + + @description('Required. The format of Cognitive Services account deployment model.') + format: string + + @description('Required. The version of Cognitive Services account deployment model.') + version: string + } + + @description('Optional. The resource model definition representing SKU.') + sku: { + @description('Required. The name of the resource model definition representing SKU.') + name: string + + @description('Optional. The capacity of the resource model definition representing SKU.') + capacity: int? + }? + + @description('Optional. The name of RAI policy.') + raiPolicyName: string? +}[]? diff --git a/avm/res/cognitive-services/account/main.json b/avm/res/cognitive-services/account/main.json index db28c215b9..7c8c756cb7 100644 --- a/avm/res/cognitive-services/account/main.json +++ b/avm/res/cognitive-services/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "1728463055074429069" + "templateHash": "2360701930449304487" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service.", @@ -475,6 +475,77 @@ } }, "nullable": true + }, + "deploymentsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + } + } + }, + "nullable": true } }, "parameters": { @@ -685,6 +756,12 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "deployments": { + "$ref": "#/definitions/deploymentsType", + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } } }, "variables": { @@ -803,6 +880,25 @@ "cMKUserAssignedIdentity" ] }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku')))]", + "dependsOn": [ + "cognitiveService" + ] + }, "cognitiveService_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", diff --git a/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep new file mode 100644 index 0000000000..7ee3f5be51 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/ai-model-deployment/main.test.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +metadata name = 'Using `deployments` in parameter set' +metadata description = 'This instance deploys the module with the AI model deployment feature.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csad' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}-ai' + params: { + name: '${namePrefix}${serviceShort}002' + kind: 'AIServices' + location: resourceLocation + customSubDomainName: '${namePrefix}x${serviceShort}ai' + deployments: [ + { + name: 'gpt-35-turbo' + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0301' + } + sku: { + name: 'Standard' + capacity: 10 + } + } + ] + } + } +] From b5d76b9c9931ba130db315643edcfc064e855bd2 Mon Sep 17 00:00:00 2001 From: Zach Trocinski <30884663+oZakari@users.noreply.github.com> Date: Wed, 17 Apr 2024 03:50:38 -0500 Subject: [PATCH 44/66] fix: Align container-app to WAF Reliability requirements (#1689) ## Description For container-apps, set the scaleMinReplicas to 3 & set scaleMaxReplicas to 10 to align to WAF Reliability Review. Closes #1188 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.app.container-app](https://github.com/oZakari/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml/badge.svg?branch=container-app-reliability-review)](https://github.com/oZakari/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Maher Aldineh Co-authored-by: Maher Aldineh --- avm/res/app/container-app/README.md | 8 ++++---- avm/res/app/container-app/main.bicep | 6 +++--- avm/res/app/container-app/main.json | 10 +++++----- avm/res/app/container-app/version.json | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/avm/res/app/container-app/README.md b/avm/res/app/container-app/README.md index d55ef832d9..236de18344 100644 --- a/avm/res/app/container-app/README.md +++ b/avm/res/app/container-app/README.md @@ -481,7 +481,7 @@ module containerApp 'br/public:avm/res/app/container-app:' = { | [`revisionSuffix`](#parameter-revisionsuffix) | string | User friendly suffix that is appended to the revision name. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`scaleMaxReplicas`](#parameter-scalemaxreplicas) | int | Maximum number of container replicas. Defaults to 10 if not set. | -| [`scaleMinReplicas`](#parameter-scaleminreplicas) | int | Minimum number of container replicas. | +| [`scaleMinReplicas`](#parameter-scaleminreplicas) | int | Minimum number of container replicas. Defaults to 3 if not set. | | [`scaleRules`](#parameter-scalerules) | array | Scaling rules. | | [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. | | [`tags`](#parameter-tags) | object | Tags of the resource. | @@ -808,15 +808,15 @@ Maximum number of container replicas. Defaults to 10 if not set. - Required: No - Type: int -- Default: `1` +- Default: `10` ### Parameter: `scaleMinReplicas` -Minimum number of container replicas. +Minimum number of container replicas. Defaults to 3 if not set. - Required: No - Type: int -- Default: `0` +- Default: `3` ### Parameter: `scaleRules` diff --git a/avm/res/app/container-app/main.bicep b/avm/res/app/container-app/main.bicep index 744ad3411a..7ef6ed7246 100644 --- a/avm/res/app/container-app/main.bicep +++ b/avm/res/app/container-app/main.bicep @@ -27,10 +27,10 @@ param ingressAllowInsecure bool = true param ingressTargetPort int = 80 @description('Optional. Maximum number of container replicas. Defaults to 10 if not set.') -param scaleMaxReplicas int = 1 +param scaleMaxReplicas int = 10 -@description('Optional. Minimum number of container replicas.') -param scaleMinReplicas int = 0 +@description('Optional. Minimum number of container replicas. Defaults to 3 if not set.') +param scaleMinReplicas int = 3 @description('Optional. Scaling rules.') param scaleRules array = [] diff --git a/avm/res/app/container-app/main.json b/avm/res/app/container-app/main.json index 7ec3817453..97c1bf140a 100644 --- a/avm/res/app/container-app/main.json +++ b/avm/res/app/container-app/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2491578115600463169" + "version": "0.26.170.59819", + "templateHash": "11829581770848647649" }, "name": "Container Apps", "description": "This module deploys a Container App.", @@ -178,16 +178,16 @@ }, "scaleMaxReplicas": { "type": "int", - "defaultValue": 1, + "defaultValue": 10, "metadata": { "description": "Optional. Maximum number of container replicas. Defaults to 10 if not set." } }, "scaleMinReplicas": { "type": "int", - "defaultValue": 0, + "defaultValue": 3, "metadata": { - "description": "Optional. Minimum number of container replicas." + "description": "Optional. Minimum number of container replicas. Defaults to 3 if not set." } }, "scaleRules": { diff --git a/avm/res/app/container-app/version.json b/avm/res/app/container-app/version.json index 7fa401bdf7..9481fea58e 100644 --- a/avm/res/app/container-app/version.json +++ b/avm/res/app/container-app/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] From 3ef0a9b78b1b96cd0ab7d96b5b347fa28edf566a Mon Sep 17 00:00:00 2001 From: Richard Hooper Date: Wed, 17 Apr 2024 20:56:05 +0100 Subject: [PATCH 45/66] feat: Enable cost analysis add-on in managed cluster module (#1682) ## Description This pull request adds the ability to enable the newly GA cost analysis feature of AKS. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg)](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Signed-off-by: PixelRobots <22979170+PixelRobots@users.noreply.github.com> --- avm/res/container-service/managed-cluster/README.md | 9 +++++++++ avm/res/container-service/managed-cluster/main.bicep | 10 +++++++++- avm/res/container-service/managed-cluster/main.json | 11 +++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index 9c6755ddb5..e5154ea6db 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -1501,6 +1501,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster: Date: Thu, 18 Apr 2024 00:41:35 +0200 Subject: [PATCH 46/66] fix: Unorphaned res\insights\activity-log-alert module as @donk-msft recently became the owner of this module. (#1692) ## Description New owner to orphaned module changes. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.insights.activity-log-alert](https://github.com/donk-msft/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml/badge.svg?branch=feat-avm%2Fres%2Finsights%2Factivity-log-alert)](https://github.com/donk-msft/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml) | ## Type of Change - [X] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings Co-authored-by: DonKoning --- avm/res/insights/activity-log-alert/ORPHANED.md | 4 ---- avm/res/insights/activity-log-alert/README.md | 5 ----- 2 files changed, 9 deletions(-) delete mode 100644 avm/res/insights/activity-log-alert/ORPHANED.md diff --git a/avm/res/insights/activity-log-alert/ORPHANED.md b/avm/res/insights/activity-log-alert/ORPHANED.md deleted file mode 100644 index ef8fa911d2..0000000000 --- a/avm/res/insights/activity-log-alert/ORPHANED.md +++ /dev/null @@ -1,4 +0,0 @@ -⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ - -- Only security and bug fixes are being handled by the AVM core team at present. -- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! \ No newline at end of file diff --git a/avm/res/insights/activity-log-alert/README.md b/avm/res/insights/activity-log-alert/README.md index 0917daf0a2..95fff9ed23 100644 --- a/avm/res/insights/activity-log-alert/README.md +++ b/avm/res/insights/activity-log-alert/README.md @@ -1,10 +1,5 @@ # Activity Log Alerts `[Microsoft.Insights/activityLogAlerts]` -> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ -> -> - Only security and bug fixes are being handled by the AVM core team at present. -> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! - This module deploys an Activity Log Alert. ## Navigation From 1c315417b3f27c4a8f170d0db04f4bb7dcd9bcff Mon Sep 17 00:00:00 2001 From: Ilhaan Rasheed Date: Wed, 17 Apr 2024 22:50:09 -0700 Subject: [PATCH 47/66] fix: aks module ARM template (#1695) ## Description Updating main.json for AKS module with latest changes in main branch. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/ilhaan/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=fix-aks-module-armtemplate-apr2024)](https://github.com/ilhaan/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/container-service/managed-cluster/main.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/avm/res/container-service/managed-cluster/main.json b/avm/res/container-service/managed-cluster/main.json index 3fdbdb4db4..f2c24f7405 100644 --- a/avm/res/container-service/managed-cluster/main.json +++ b/avm/res/container-service/managed-cluster/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "11298093745131885845" + "templateHash": "3806996475520060110" }, "name": "Azure Kubernetes Service (AKS) Managed Clusters", "description": "This module deploys an Azure Kubernetes Service (AKS) Managed Cluster.", @@ -1528,6 +1528,11 @@ "agentPoolProfiles": "[parameters('primaryAgentPoolProfile')]", "linuxProfile": "[if(not(empty(parameters('sshPublicKey'))), createObject('adminUsername', parameters('adminUsername'), 'ssh', createObject('publicKeys', createArray(createObject('keyData', coalesce(parameters('sshPublicKey'), ''))))), null())]", "servicePrincipalProfile": "[parameters('aksServicePrincipalProfile')]", + "metricsProfile": { + "costAnalysis": { + "enabled": "[if(equals(parameters('skuTier'), 'free'), false(), parameters('costAnalysisEnabled'))]" + } + }, "ingressProfile": { "webAppRouting": { "enabled": "[parameters('webApplicationRoutingEnabled')]", @@ -1640,7 +1645,7 @@ "enabled": "[parameters('enableStorageProfileBlobCSIDriver')]" }, "diskCSIDriver": { - "enabled": "[if(equals(parameters('costAnalysisEnabled'), true()), true(), parameters('enableStorageProfileDiskCSIDriver'))]" + "enabled": "[if(and(equals(parameters('costAnalysisEnabled'), true()), not(equals(parameters('skuTier'), 'free'))), true(), parameters('enableStorageProfileDiskCSIDriver'))]" }, "fileCSIDriver": { "enabled": "[parameters('enableStorageProfileFileCSIDriver')]" From 6e086ba1bd31e3199954fbead329cf88d621a2ca Mon Sep 17 00:00:00 2001 From: Felix Borst <17405838+fblix@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:51:10 +0200 Subject: [PATCH 48/66] fix: Failing Pipeline: Nullable types are not allowed (#1698) ## Description This PR solves the failing pipelines currently present in main. The fix includes changing the `networkAclsType` to not be nullable anymore. Fixes #1453 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/fblix/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Ffblix%2F1453)](https://github.com/fblix/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Felix Borst --- avm/res/storage/storage-account/README.md | 14 +++++ .../container/immutability-policy/main.json | 4 +- .../blob-service/container/main.json | 8 +-- .../storage-account/blob-service/main.json | 12 ++--- .../storage-account/file-service/main.json | 12 ++--- .../file-service/share/main.json | 8 +-- .../storage-account/local-user/main.json | 4 +- avm/res/storage/storage-account/main.bicep | 2 +- avm/res/storage/storage-account/main.json | 52 +++++++++---------- .../management-policy/main.json | 4 +- .../storage-account/queue-service/main.json | 8 +-- .../queue-service/queue/main.json | 4 +- .../storage-account/table-service/main.json | 8 +-- .../table-service/table/main.json | 4 +- .../tests/e2e/defaults/main.test.bicep | 2 +- 15 files changed, 80 insertions(+), 66 deletions(-) diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 4bdbae8b8e..83fba9bd49 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -181,6 +181,11 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { name: 'ssamin001' // Non-required parameters allowBlobPublicAccess: false + location: '' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } } } ``` @@ -204,6 +209,15 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { // Non-required parameters "allowBlobPublicAccess": { "value": false + }, + "location": { + "value": "" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny" + } } } } diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index 91a31edc0d..a45e614327 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index a41cc7b8ce..0d487d2827 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3297137219061666309" + "version": "0.26.170.59819", + "templateHash": "3805384021483033369" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -274,8 +274,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 2ac6992281..1ffa10179f 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12175020972967228537" + "version": "0.26.170.59819", + "templateHash": "7278814029590745003" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -403,8 +403,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3297137219061666309" + "version": "0.26.170.59819", + "templateHash": "3805384021483033369" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -672,8 +672,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 38dcbcb7c7..31a38bc821 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13860799899517512764" + "version": "0.26.170.59819", + "templateHash": "4306536926065797375" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -286,8 +286,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18405917853511220559" + "version": "0.26.170.59819", + "templateHash": "13618261904162439978" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -486,8 +486,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16221226236637657314" + "version": "0.26.170.59819", + "templateHash": "6057169747302051267" } }, "parameters": { diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index 813cd74fbd..5e5f8ff5c6 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18405917853511220559" + "version": "0.26.170.59819", + "templateHash": "13618261904162439978" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -205,8 +205,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16221226236637657314" + "version": "0.26.170.59819", + "templateHash": "6057169747302051267" } }, "parameters": { diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index 07d5ea2520..c5936241dd 100644 --- a/avm/res/storage/storage-account/local-user/main.json +++ b/avm/res/storage/storage-account/local-user/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3388645117303533667" + "version": "0.26.170.59819", + "templateHash": "14593329616022153178" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 1b4437ab21..58ba1d817e 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -701,7 +701,7 @@ type networkAclsType = { @description('Required. Specifies the default action of allow or deny when no other rules match.') defaultAction: ('Allow' | 'Deny') -}? +} type privateEndpointType = { @description('Optional. The name of the private endpoint.') diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index d4a1fa422f..91eb9c9f57 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13878793352750035767" + "version": "0.26.170.59819", + "templateHash": "8863225181818962940" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -1691,8 +1691,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14052382142427178929" + "version": "0.26.170.59819", + "templateHash": "4153955640795225346" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", @@ -1801,8 +1801,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3388645117303533667" + "version": "0.26.170.59819", + "templateHash": "14593329616022153178" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", @@ -2019,8 +2019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12175020972967228537" + "version": "0.26.170.59819", + "templateHash": "7278814029590745003" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2417,8 +2417,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "3297137219061666309" + "version": "0.26.170.59819", + "templateHash": "3805384021483033369" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2686,8 +2686,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2429139364933838439" + "version": "0.26.170.59819", + "templateHash": "12849754295459852309" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", @@ -2865,8 +2865,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13860799899517512764" + "version": "0.26.170.59819", + "templateHash": "4306536926065797375" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -3146,8 +3146,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18405917853511220559" + "version": "0.26.170.59819", + "templateHash": "13618261904162439978" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -3346,8 +3346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "16221226236637657314" + "version": "0.26.170.59819", + "templateHash": "6057169747302051267" } }, "parameters": { @@ -3615,8 +3615,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13677013226831946461" + "version": "0.26.170.59819", + "templateHash": "4494965320132257914" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -3860,8 +3860,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17219066722763101567" + "version": "0.26.170.59819", + "templateHash": "2870814699283520878" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -4117,8 +4117,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17373442840517371502" + "version": "0.26.170.59819", + "templateHash": "6806949296172999226" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -4359,8 +4359,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10226619499666007193" + "version": "0.26.170.59819", + "templateHash": "5583637725561111612" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index 323df90a49..d751651d08 100644 --- a/avm/res/storage/storage-account/management-policy/main.json +++ b/avm/res/storage/storage-account/management-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14052382142427178929" + "version": "0.26.170.59819", + "templateHash": "4153955640795225346" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index ea2dd644a3..b1ddfd7258 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "13677013226831946461" + "version": "0.26.170.59819", + "templateHash": "4494965320132257914" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -250,8 +250,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17219066722763101567" + "version": "0.26.170.59819", + "templateHash": "2870814699283520878" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index 981f57b5d0..7463266ec4 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17219066722763101567" + "version": "0.26.170.59819", + "templateHash": "2870814699283520878" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index bb058e623a..cd4f72dbef 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "17373442840517371502" + "version": "0.26.170.59819", + "templateHash": "6806949296172999226" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -247,8 +247,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10226619499666007193" + "version": "0.26.170.59819", + "templateHash": "5583637725561111612" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index 1ba6eab43d..0abcc4d463 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "10226619499666007193" + "version": "0.26.170.59819", + "templateHash": "5583637725561111612" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep b/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep index 31e888a7ff..c6de9c25bf 100644 --- a/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep +++ b/avm/res/storage/storage-account/tests/e2e/defaults/main.test.bicep @@ -45,7 +45,7 @@ module testDeployment '../../../main.bicep' = [ allowBlobPublicAccess: false location: resourceLocation networkAcls: { - defaultAction: 'Allow' + defaultAction: 'Deny' bypass: 'AzureServices' } } From d9a53b87e12bb7ea991d45c8679bde27481b25cb Mon Sep 17 00:00:00 2001 From: Nate Arnold Date: Thu, 18 Apr 2024 12:41:58 -0600 Subject: [PATCH 49/66] feat: `avm/ptn/authorization/role-assignment` (#1641) ## Description Migrating module from CARML to AVM Closes https://github.com/Azure/Azure-Verified-Modules/issues/775 ## Pipeline Reference [![avm.ptn.authorization.role-assignment](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml/badge.svg?branch=avm%2Fptn%2Fauthorization%2Frole-assignment)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Co-authored-by: Alexander Sehr --- .github/CODEOWNERS | 3 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.ptn.authorization.role-assignment.yml | 86 +++ .../authorization/role-assignment/ORPHANED.md | 4 + .../authorization/role-assignment/README.md | 567 +++++++++++++++ .../authorization/role-assignment/main.bicep | 127 ++++ .../authorization/role-assignment/main.json | 666 ++++++++++++++++++ .../modules/management-group.bicep | 75 ++ .../modules/resource-group.bicep | 78 ++ .../modules/subscription.bicep | 75 ++ .../tests/e2e/mg.defaults/dependencies.bicep | 13 + .../mg.defaults/interim.dependencies.bicep | 28 + .../tests/e2e/mg.defaults/main.test.bicep | 53 ++ .../tests/e2e/mg.max/dependencies.bicep | 13 + .../e2e/mg.max/interim.dependencies.bicep | 28 + .../tests/e2e/mg.max/main.test.bicep | 55 ++ .../tests/e2e/rg.default/dependencies.bicep | 13 + .../tests/e2e/rg.default/main.test.bicep | 65 ++ .../tests/e2e/rg.max/dependencies.bicep | 13 + .../tests/e2e/rg.max/main.test.bicep | 64 ++ .../tests/e2e/sub.default/dependencies.bicep | 13 + .../sub.default/interim.dependencies.bicep | 28 + .../tests/e2e/sub.default/main.test.bicep | 65 ++ .../tests/e2e/sub.max/dependencies.bicep | 13 + .../e2e/sub.max/interim.dependencies.bicep | 28 + .../tests/e2e/sub.max/main.test.bicep | 66 ++ .../role-assignment/version.json | 7 + 27 files changed, 2246 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/avm.ptn.authorization.role-assignment.yml create mode 100644 avm/ptn/authorization/role-assignment/ORPHANED.md create mode 100644 avm/ptn/authorization/role-assignment/README.md create mode 100644 avm/ptn/authorization/role-assignment/main.bicep create mode 100644 avm/ptn/authorization/role-assignment/main.json create mode 100644 avm/ptn/authorization/role-assignment/modules/management-group.bicep create mode 100644 avm/ptn/authorization/role-assignment/modules/resource-group.bicep create mode 100644 avm/ptn/authorization/role-assignment/modules/subscription.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep create mode 100644 avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep create mode 100644 avm/ptn/authorization/role-assignment/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bf9aa06b23..7290c25df5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,8 @@ /scripts/ @Azure/bicep-admins @Azure/avm-core-team-technical-bicep /avm/ @Azure/avm-core-team-technical-bicep /avm/utilities/ @Azure/avm-core-team-technical-bicep -/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep +#/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep /avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/api-management/service/ @Azure/avm-res-apimanagement-service-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/app/container-app/ @Azure/avm-res-app-containerapp-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 77d58e66d1..681087787b 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -38,6 +38,7 @@ body: description: Which existing AVM module is this issue related to? options: - "" + - "avm/ptn/authorization/role-assignment" # - "avm/ptn/avd-lza/insights" # - "avm/ptn/avd-lza/management-plane" # - "avm/ptn/avd-lza/networking" diff --git a/.github/workflows/avm.ptn.authorization.role-assignment.yml b/.github/workflows/avm.ptn.authorization.role-assignment.yml new file mode 100644 index 0000000000..c9ad828152 --- /dev/null +++ b/.github/workflows/avm.ptn.authorization.role-assignment.yml @@ -0,0 +1,86 @@ +name: "avm.ptn.authorization.role-assignment" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.authorization.role-assignment.yml" + - "avm/ptn/authorization/role-assignment/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/authorization/role-assignment" + workflowPath: ".github/workflows/avm.ptn.authorization.role-assignment.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/ptn/authorization/role-assignment/ORPHANED.md b/avm/ptn/authorization/role-assignment/ORPHANED.md new file mode 100644 index 0000000000..ef8fa911d2 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/ORPHANED.md @@ -0,0 +1,4 @@ +⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ + +- Only security and bug fixes are being handled by the AVM core team at present. +- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! \ No newline at end of file diff --git a/avm/ptn/authorization/role-assignment/README.md b/avm/ptn/authorization/role-assignment/README.md new file mode 100644 index 0000000000..bbe4b6d33e --- /dev/null +++ b/avm/ptn/authorization/role-assignment/README.md @@ -0,0 +1,567 @@ +# Role Assignments (All scopes) `[Microsoft.Authorization/roleAssignments]` + +> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ +> +> - Only security and bug fixes are being handled by the AVM core team at present. +> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! + +This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/authorization/role-assignment:`. + +- [Role Assignments (Management Group scope)](#example-1-role-assignments-management-group-scope) +- [Role Assignments (Management Group scope)](#example-2-role-assignments-management-group-scope) +- [Role Assignments (Resource Group scope)](#example-3-role-assignments-resource-group-scope) +- [Role Assignments (Resource Group)](#example-4-role-assignments-resource-group) +- [Role Assignments (Subscription scope)](#example-5-role-assignments-subscription-scope) +- [Role Assignments (Subscription scope)](#example-6-role-assignments-subscription-scope) + +### Example 1: _Role Assignments (Management Group scope)_ + +This module deploys a Role Assignment at a Management Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Resource Policy Contributor' + // Non-required parameters + location: '' + principalType: 'ServicePrincipal' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Resource Policy Contributor" + }, + // Non-required parameters + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +### Example 2: _Role Assignments (Management Group scope)_ + +This module deploys a Role Assignment at a Management Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Management Group Reader' + // Non-required parameters + description: 'Role Assignment (management group scope)' + location: '' + managementGroupId: '' + principalType: 'ServicePrincipal' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Management Group Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (management group scope)" + }, + "location": { + "value": "" + }, + "managementGroupId": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +### Example 3: _Role Assignments (Resource Group scope)_ + +This module deploys a Role Assignment at a Resource Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + // Non-required parameters + location: '' + principalType: 'ServicePrincipal' + resourceGroupName: '' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11" + }, + // Non-required parameters + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _Role Assignments (Resource Group)_ + +This module deploys a Role Assignment at a Resource Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Reader' + // Non-required parameters + description: 'Role Assignment (resource group scope)' + location: '' + principalType: 'ServicePrincipal' + resourceGroupName: '' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (resource group scope)" + }, + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Role Assignments (Subscription scope)_ + +This module deploys a Role Assignment at a Subscription scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: '' + // Non-required parameters + location: '' + principalType: 'ServicePrincipal' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 6: _Role Assignments (Subscription scope)_ + +This module deploys a Role Assignment at a Subscription scope using common parameters. + + +

+ +via Bicep module + +```bicep +module roleAssignment 'br/public:avm/ptn/authorization/role-assignment:' = { + name: 'roleAssignmentDeployment' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Reader' + // Non-required parameters + description: 'Role Assignment (subscription scope)' + location: '' + principalType: 'ServicePrincipal' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "principalId": { + "value": "" + }, + "roleDefinitionIdOrName": { + "value": "Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (subscription scope)" + }, + "location": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-principalid) | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| [`roleDefinitionIdOrName`](#parameter-roledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-condition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. | +| [`conditionVersion`](#parameter-conditionversion) | string | Version of the condition. Currently accepted value is "2.0". | +| [`delegatedManagedIdentityResourceId`](#parameter-delegatedmanagedidentityresourceid) | string | ID of the delegated managed identity resource. | +| [`description`](#parameter-description) | string | The description of the role assignment. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location deployment metadata. | +| [`managementGroupId`](#parameter-managementgroupid) | string | Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| [`principalType`](#parameter-principaltype) | string | The principal type of the assigned principal ID. | +| [`resourceGroupName`](#parameter-resourcegroupname) | string | 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. | +| [`subscriptionId`](#parameter-subscriptionid) | string | 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. | + +### Parameter: `principalId` + +The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). + +- Required: Yes +- Type: string + +### Parameter: `roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `conditionVersion` + +Version of the condition. Currently accepted value is "2.0". + +- Required: No +- Type: string +- Default: `'2.0'` +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `delegatedManagedIdentityResourceId` + +ID of the delegated managed identity resource. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `description` + +The description of the role assignment. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location deployment metadata. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +### Parameter: `managementGroupId` + +Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. + +- Required: No +- Type: string +- Default: `[managementGroup().name]` + +### Parameter: `principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Default: `''` +- Allowed: + ```Bicep + [ + '' + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `resourceGroupName` + +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. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `subscriptionId` + +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. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/authorization/role-assignment/main.bicep b/avm/ptn/authorization/role-assignment/main.bicep new file mode 100644 index 0000000000..36669e0e95 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/main.bicep @@ -0,0 +1,127 @@ +metadata name = 'Role Assignments (All scopes)' +metadata description = 'This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.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\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.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.') +param resourceGroupName string = '' + +@sys.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.') +param subscriptionId string = '' + +@sys.description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.ptn.authorization-roleassignment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + location:location + } + +module roleAssignment_mg 'modules/management-group.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-MG-Module' + scope: managementGroup(managementGroupId) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + managementGroupId: managementGroupId + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + } +} + +module roleAssignment_sub 'modules/subscription.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-Sub-Module' + scope: subscription(subscriptionId) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + subscriptionId: subscriptionId + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + } +} + +module roleAssignment_rg 'modules/resource-group.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-RoleAssignment-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + roleDefinitionIdOrName: roleDefinitionIdOrName + principalId: principalId + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + description: !empty(description) ? description : '' + principalType: !empty(principalType) ? principalType : '' + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : '' + conditionVersion: conditionVersion + condition: !empty(condition) ? condition : '' + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.name : roleAssignment_rg.outputs.name) + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.resourceId : roleAssignment_rg.outputs.resourceId) + +@sys.description('The scope this Role Assignment applies to.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? roleAssignment_sub.outputs.scope : roleAssignment_rg.outputs.scope) diff --git a/avm/ptn/authorization/role-assignment/main.json b/avm/ptn/authorization/role-assignment/main.json new file mode 100644 index 0000000000..d8570e9088 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/main.json @@ -0,0 +1,666 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "3360936249154372689" + }, + "name": "Role Assignments (All scopes)", + "description": "This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location deployment metadata." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.authorization-roleassignment.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + }, + "location": "[parameters('location')]" + }, + { + "condition": "[and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))]", + "scope": "[format('Microsoft.Management/managementGroups/{0}', parameters('managementGroupId'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "roleDefinitionIdOrName": { + "value": "[parameters('roleDefinitionIdOrName')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "managementGroupId": { + "value": "[parameters('managementGroupId')]" + }, + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "principalType": "[if(not(empty(parameters('principalType'))), createObject('value', parameters('principalType')), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), createObject('value', parameters('delegatedManagedIdentityResourceId')), createObject('value', ''))]", + "conditionVersion": { + "value": "[parameters('conditionVersion')]" + }, + "condition": "[if(not(empty(parameters('condition'))), createObject('value', parameters('condition')), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "13749459126745145624" + }, + "name": "Role Assignments (Management Group scope)", + "description": "This module deploys a Role Assignment at a Management Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Management Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d')]" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('managementGroupId'), variables('roleDefinitionIdVar'), parameters('principalId'))]", + "properties": { + "roleDefinitionId": "[variables('roleDefinitionIdVar')]", + "principalId": "[parameters('principalId')]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[guid(parameters('managementGroupId'), variables('roleDefinitionIdVar'), parameters('principalId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/roleAssignments', guid(parameters('managementGroupId'), variables('roleDefinitionIdVar'), parameters('principalId')))]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[resourceId('Microsoft.Management/managementGroups', parameters('managementGroupId'))]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "roleDefinitionIdOrName": { + "value": "[parameters('roleDefinitionIdOrName')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "principalType": "[if(not(empty(parameters('principalType'))), createObject('value', parameters('principalType')), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), createObject('value', parameters('delegatedManagedIdentityResourceId')), createObject('value', ''))]", + "conditionVersion": { + "value": "[parameters('conditionVersion')]" + }, + "condition": "[if(not(empty(parameters('condition'))), createObject('value', parameters('condition')), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4516670639800961845" + }, + "name": "Role Assignments (Subscription scope)", + "description": "This module deploys a Role Assignment at a Subscription scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), variables('roleDefinitionIdVar'), parameters('principalId'))]", + "properties": { + "roleDefinitionId": "[variables('roleDefinitionIdVar')]", + "principalId": "[parameters('principalId')]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[guid(parameters('subscriptionId'), variables('roleDefinitionIdVar'), parameters('principalId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[subscriptionResourceId('Microsoft.Authorization/roleAssignments', guid(parameters('subscriptionId'), variables('roleDefinitionIdVar'), parameters('principalId')))]" + }, + "subscriptionName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the role assignment was applied at." + }, + "value": "[subscription().displayName]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[subscription().id]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('resourceGroupName'))), not(empty(parameters('subscriptionId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "roleDefinitionIdOrName": { + "value": "[parameters('roleDefinitionIdOrName')]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "resourceGroupName": { + "value": "[parameters('resourceGroupName')]" + }, + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "principalType": "[if(not(empty(parameters('principalType'))), createObject('value', parameters('principalType')), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), createObject('value', parameters('delegatedManagedIdentityResourceId')), createObject('value', ''))]", + "conditionVersion": { + "value": "[parameters('conditionVersion')]" + }, + "condition": "[if(not(empty(parameters('condition'))), createObject('value', parameters('condition')), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "7241874480439813582" + }, + "name": "Role Assignments (Resource Group scope)", + "description": "This module deploys a Role Assignment at a Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Optional. Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the delegated managed identity resource." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to." + } + }, + "conditionVersion": { + "type": "string", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition. Currently accepted value is \"2.0\"." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), parameters('resourceGroupName'), variables('roleDefinitionIdVar'), parameters('principalId'))]", + "properties": { + "roleDefinitionId": "[variables('roleDefinitionIdVar')]", + "principalId": "[parameters('principalId')]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[guid(parameters('subscriptionId'), parameters('resourceGroupName'), variables('roleDefinitionIdVar'), parameters('principalId'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[resourceId('Microsoft.Authorization/roleAssignments', guid(parameters('subscriptionId'), parameters('resourceGroupName'), variables('roleDefinitionIdVar'), parameters('principalId')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the role assignment was applied at." + }, + "value": "[resourceGroup().name]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[resourceGroup().id]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value))]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this Role Assignment applies to." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.scope.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.scope.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-RoleAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.scope.value))]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/authorization/role-assignment/modules/management-group.bicep b/avm/ptn/authorization/role-assignment/modules/management-group.bicep new file mode 100644 index 0000000000..b1a51d40f9 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/modules/management-group.bicep @@ -0,0 +1,75 @@ +metadata name = 'Role Assignments (Management Group scope)' +metadata description = 'This module deploys a Role Assignment at a Management Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.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\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Management Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d') +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managementGroupId, roleDefinitionIdVar, principalId) + properties: { + roleDefinitionId: roleDefinitionIdVar + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = roleAssignment.id + +@sys.description('The scope this Role Assignment applies to.') +output scope string = az.resourceId('Microsoft.Management/managementGroups', managementGroupId) diff --git a/avm/ptn/authorization/role-assignment/modules/resource-group.bicep b/avm/ptn/authorization/role-assignment/modules/resource-group.bicep new file mode 100644 index 0000000000..459bde3f99 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/modules/resource-group.bicep @@ -0,0 +1,78 @@ +metadata name = 'Role Assignments (Resource Group scope)' +metadata description = 'This module deploys a Role Assignment at a Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'resourceGroup' + +@sys.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\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscriptionId, resourceGroupName, roleDefinitionIdVar, principalId) + properties: { + roleDefinitionId: roleDefinitionIdVar + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = roleAssignment.id + +@sys.description('The name of the resource group the role assignment was applied at.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The scope this Role Assignment applies to.') +output scope string = resourceGroup().id diff --git a/avm/ptn/authorization/role-assignment/modules/subscription.bicep b/avm/ptn/authorization/role-assignment/modules/subscription.bicep new file mode 100644 index 0000000000..2a41bc6b07 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/modules/subscription.bicep @@ -0,0 +1,75 @@ +metadata name = 'Role Assignments (Subscription scope)' +metadata description = 'This module deploys a Role Assignment at a Subscription scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'subscription' + +@sys.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\'.') +param roleDefinitionIdOrName string + +@sys.description('Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity).') +param principalId string + +@sys.description('Optional. Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. ID of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to.') +param condition string = '' + +@sys.description('Optional. Version of the condition. Currently accepted value is "2.0".') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscriptionId, roleDefinitionIdVar, principalId) + properties: { + roleDefinitionId: roleDefinitionIdVar + principalId: principalId + description: !empty(description) ? description : null + principalType: !empty(principalType) ? any(principalType) : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + condition: !empty(condition) ? condition : null + } +} + +@sys.description('The GUID of the Role Assignment.') +output name string = roleAssignment.name + +@sys.description('The resource ID of the Role Assignment.') +output resourceId string = roleAssignment.id + +@sys.description('The name of the resource group the role assignment was applied at.') +output subscriptionName string = subscription().displayName + +@sys.description('The scope this Role Assignment applies to.') +output scope string = subscription().id diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep new file mode 100644 index 0000000000..0dfff91298 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.defaults/main.test.bicep @@ -0,0 +1,53 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Management Group scope)' +metadata description = 'This module deploys a Role Assignment at a Management Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aramgmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-${serviceShort}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Resource Policy Contributor' + principalType: 'ServicePrincipal' + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep new file mode 100644 index 0000000000..b7e9d1bd87 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/mg.max/main.test.bicep @@ -0,0 +1,55 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Management Group scope)' +metadata description = 'This module deploys a Role Assignment at a Management Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aramgmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-${serviceShort}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Management Group Reader' + description: 'Role Assignment (management group scope)' + managementGroupId: last(split(managementGroup().id, '/')) + principalType: 'ServicePrincipal' + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep new file mode 100644 index 0000000000..5681a89989 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep new file mode 100644 index 0000000000..99cebb3035 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.default/main.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Resource Group scope)' +metadata description = 'This module deploys a Role Assignment at a Resource Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'arargmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } + + +} +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup(subscriptionId, resourceGroupName) + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep new file mode 100644 index 0000000000..5681a89989 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep new file mode 100644 index 0000000000..b154901d94 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/rg.max/main.test.bicep @@ -0,0 +1,64 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Resource Group)' +metadata description = 'This module deploys a Role Assignment at a Resource Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'arargmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup(subscriptionId, resourceGroupName) + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Reader' + description: 'Role Assignment (resource group scope)' + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep new file mode 100644 index 0000000000..4b1543c60b --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.default/main.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Subscription scope)' +metadata description = 'This module deploys a Role Assignment at a Subscription scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'arasubmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= + + +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + } +} diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep new file mode 100644 index 0000000000..d367770432 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep new file mode 100644 index 0000000000..55ada1deea --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep new file mode 100644 index 0000000000..0f41d75951 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/tests/e2e/sub.max/main.test.bicep @@ -0,0 +1,66 @@ +targetScope = 'managementGroup' +metadata name = 'Role Assignments (Subscription scope)' +metadata description = 'This module deploys a Role Assignment at a Subscription scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'arasubmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= + +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } + + +} +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Reader' + description: 'Role Assignment (subscription scope)' + principalType: 'ServicePrincipal' + location: resourceLocation + subscriptionId: subscriptionId + } +} diff --git a/avm/ptn/authorization/role-assignment/version.json b/avm/ptn/authorization/role-assignment/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/ptn/authorization/role-assignment/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From caf42624a86138df6826559564e9d572dc92d872 Mon Sep 17 00:00:00 2001 From: Richard Hooper Date: Thu, 18 Apr 2024 22:00:15 +0100 Subject: [PATCH 50/66] feat: add option for keda addon container-service - `avm/res/container-service/managed-cluster` (#1691) ## Description This is adding in the option to enable Keda on an AKS cluster. I would probably do this one after https://github.com/Azure/bicep-registry-modules/pull/1682 as i used my main branch that has the cost anlayasis changes in it. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=feat-Keda)](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml?query=branch%3Afeat-Keda) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Signed-off-by: PixelRobots <22979170+PixelRobots@users.noreply.github.com> --- .../container-service/managed-cluster/README.md | 9 +++++++++ .../container-service/managed-cluster/main.bicep | 8 ++++++++ .../container-service/managed-cluster/main.json | 14 +++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index e5154ea6db..43e75d0fb7 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -1536,6 +1536,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster: Date: Thu, 18 Apr 2024 18:34:05 -0600 Subject: [PATCH 51/66] feat: avm/ptn/authorization/policy assignment2 (#1706) ## Description Taking ownership of authorization policy-assignment pattern module ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ X] Update to documentation ## Checklist - [ X] I'm sure there are no other open Pull Requests for the same update/change - [ X] I have run `Set-AVMModule` locally to generate the supporting module files. - [ X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- avm/ptn/authorization/role-assignment/ORPHANED.md | 4 ---- avm/ptn/authorization/role-assignment/README.md | 5 ----- 2 files changed, 9 deletions(-) delete mode 100644 avm/ptn/authorization/role-assignment/ORPHANED.md diff --git a/avm/ptn/authorization/role-assignment/ORPHANED.md b/avm/ptn/authorization/role-assignment/ORPHANED.md deleted file mode 100644 index ef8fa911d2..0000000000 --- a/avm/ptn/authorization/role-assignment/ORPHANED.md +++ /dev/null @@ -1,4 +0,0 @@ -⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ - -- Only security and bug fixes are being handled by the AVM core team at present. -- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! \ No newline at end of file diff --git a/avm/ptn/authorization/role-assignment/README.md b/avm/ptn/authorization/role-assignment/README.md index bbe4b6d33e..dd1e9be0ca 100644 --- a/avm/ptn/authorization/role-assignment/README.md +++ b/avm/ptn/authorization/role-assignment/README.md @@ -1,10 +1,5 @@ # Role Assignments (All scopes) `[Microsoft.Authorization/roleAssignments]` -> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ -> -> - Only security and bug fixes are being handled by the AVM core team at present. -> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! - This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope. ## Navigation From f3307c719961db3b27dc8e2f5741ad1bd7552bba Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Fri, 19 Apr 2024 09:25:58 +0200 Subject: [PATCH 52/66] fix: change secret reference to the correct name (#1707) --- .github/workflows/avm.platform.manage-workflow-issue.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/avm.platform.manage-workflow-issue.yml index 9c80793d18..2125028fe3 100644 --- a/.github/workflows/avm.platform.manage-workflow-issue.yml +++ b/.github/workflows/avm.platform.manage-workflow-issue.yml @@ -18,8 +18,8 @@ jobs: - uses: tibdex/github-app-token@v2 id: generate-token with: - app_id: ${{ secrets.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} + app_id: ${{ secrets.TEAM_LINTER_APP_ID }} + private_key: ${{ secrets.TEAM_LINTER_PRIVATE_KEY }} - name: Manage issues shell: pwsh env: From 4f0f0a04ced5080d4c616e984e78827a95eee918 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:45:44 +0200 Subject: [PATCH 53/66] fix: add missing folder reference (#1712) Add missing folder reference, so script can be found. --- .../pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 index 42bbfda099..3ef3637972 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 @@ -35,6 +35,7 @@ function Set-AvmGitHubIssueOwnerConfig { # Loading helper functions . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Get-AvmCsvData.ps1') + . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'platform' 'helper' 'Add-GithubIssueToProject.ps1') $issue = gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --json 'author,title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 From e4baf3f37bab7dff7879e9c70ca5a4053616a447 Mon Sep 17 00:00:00 2001 From: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:42:05 +0200 Subject: [PATCH 54/66] fix: minor fixes in issue automation (#1717) --- .github/workflows/avm.platform.manage-workflow-issue.yml | 3 ++- .../avm.platform.set-avm-github-issue-owner-config.yml | 2 +- .github/workflows/avm.platform.sync-avm-modules-list.yml | 1 + .../pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 | 1 + .../pipelines/platform/helper/Add-GithubIssueToProject.ps1 | 2 +- avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 | 1 - 6 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/avm.platform.manage-workflow-issue.yml b/.github/workflows/avm.platform.manage-workflow-issue.yml index 2125028fe3..9b36fb37ad 100644 --- a/.github/workflows/avm.platform.manage-workflow-issue.yml +++ b/.github/workflows/avm.platform.manage-workflow-issue.yml @@ -1,4 +1,5 @@ -name: "avm.platform.manage-workflow-issue" +# Workflow for creating issues for failing workflows +name: .Platform - Manage workflow issue on: schedule: diff --git a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml index 48cc265747..84f3f7b138 100644 --- a/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml +++ b/.github/workflows/avm.platform.set-avm-github-issue-owner-config.yml @@ -1,5 +1,5 @@ # Workflow for notifying and assigning issues on creation -name: avm.platform.set-avm-github-issue-owner-config +name: .Platform - Set AVM GitHub issue owner config on: issues: diff --git a/.github/workflows/avm.platform.sync-avm-modules-list.yml b/.github/workflows/avm.platform.sync-avm-modules-list.yml index 7557309692..06c584a7ff 100644 --- a/.github/workflows/avm.platform.sync-avm-modules-list.yml +++ b/.github/workflows/avm.platform.sync-avm-modules-list.yml @@ -1,3 +1,4 @@ +# Workflow to create an issue, if AVM module list is not in sync with CSV file name: .Platform - Sync AVM module list on: diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index a2c1a5f98d..fed872e51d 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -95,6 +95,7 @@ function Set-AvmGithubIssueForWorkflow { $ProjectNumber = 538 # AVM - Issue Triage $comment = @" > [!IMPORTANT] +> This module is currently orphaned (has no owner), therefore expect a higher response time. > @Azure/avm-core-team-technical-bicep, the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. "@ diff --git a/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 index cd9d0222e4..ea303f47e3 100644 --- a/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 +++ b/avm/utilities/pipelines/platform/helper/Add-GithubIssueToProject.ps1 @@ -44,7 +44,7 @@ function Add-GithubIssueToProject { }' -f organization=$Organization -F number=$ProjectNumber | ConvertFrom-Json -Depth 10 $ProjectId = $Project.data.organization.projectV2.id - $IssueId = (gh issue view $IssueUrl --repo $Repo --json 'id' | ConvertFrom-Json -Depth 100).id + $IssueId = (gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --repo $Repo --json 'id' | ConvertFrom-Json -Depth 100).id gh api graphql -f query=' mutation($project:ID!, $issue:ID!) { diff --git a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 index 17f6e077b9..821f476bb7 100644 --- a/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 +++ b/avm/utilities/pipelines/platform/helper/Get-AvmCsvData.ps1 @@ -30,7 +30,6 @@ Function Get-AvmCsvData { 'Bicep-Resource' { try { $unfilteredCSV = Invoke-WebRequest -Uri $BicepResourceUrl - } catch { throw 'Unable to retrieve CSV file - Check network connection.' } From 573eedd0004388ebb9e6580f0a29b26c3a39b488 Mon Sep 17 00:00:00 2001 From: Nate Arnold Date: Fri, 19 Apr 2024 09:28:05 -0600 Subject: [PATCH 55/66] feat: `avm/ptn/authorization/policy-assignment` (#1688) ## Description Migration of policy-assignment pattern module from CARML to AVM Closes https://github.com/Azure/Azure-Verified-Modules/issues/774 ## Pipeline Reference [![avm.ptn.authorization.policy-assignment](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml/badge.svg?branch=avm%2Fptn%2Fauthorization%2Fpolicy-assignment)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .github/CODEOWNERS | 3 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ...m.ptn.authorization.policy-assignment.yml} | 10 +- .../authorization/policy-assignment/README.md | 1063 +++++++++++++++++ .../policy-assignment/main.bicep | 172 +++ .../authorization/policy-assignment/main.json | 989 +++++++++++++++ .../modules/management-group.bicep | 112 ++ .../modules/resource-group.bicep | 119 ++ .../modules/subscription.bicep | 112 ++ .../tests/e2e/mg.defaults/main.test.bicep | 33 + .../tests/e2e/mg.max/main.test.bicep | 96 ++ .../tests/e2e/rg.defaults/main.test.bicep | 57 + .../tests/e2e/rg.max/dependencies.bicep | 33 + .../tests/e2e/rg.max/main.test.bicep | 127 ++ .../tests/e2e/sub.defaults/main.test.bicep | 39 + .../tests/e2e/sub.max/dependencies.bicep | 13 + .../e2e/sub.max/interim.dependencies.bicep | 28 + .../tests/e2e/sub.max/main.test.bicep | 126 ++ .../policy-assignment/version.json | 7 + 19 files changed, 3134 insertions(+), 6 deletions(-) rename .github/workflows/{avm.ptn.authorization.role-assignment.yml => avm.ptn.authorization.policy-assignment.yml} (89%) create mode 100644 avm/ptn/authorization/policy-assignment/README.md create mode 100644 avm/ptn/authorization/policy-assignment/main.bicep create mode 100644 avm/ptn/authorization/policy-assignment/main.json create mode 100644 avm/ptn/authorization/policy-assignment/modules/management-group.bicep create mode 100644 avm/ptn/authorization/policy-assignment/modules/resource-group.bicep create mode 100644 avm/ptn/authorization/policy-assignment/modules/subscription.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep create mode 100644 avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep create mode 100644 avm/ptn/authorization/policy-assignment/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7290c25df5..2851e9058b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,9 @@ /scripts/ @Azure/bicep-admins @Azure/avm-core-team-technical-bicep /avm/ @Azure/avm-core-team-technical-bicep /avm/utilities/ @Azure/avm-core-team-technical-bicep +/avm/ptn/authorization/policy-assignment/ @Azure/avm-ptn-authorization-policyassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep /avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/api-management/service/ @Azure/avm-res-apimanagement-service-module-owners-bicep @Azure/avm-core-team-technical-bicep @@ -11,7 +13,6 @@ /avm/res/app/managed-environment/ @Azure/avm-res-app-managedenvironment-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/app-configuration/configuration-store/ @Azure/avm-res-appconfiguration-configurationstore-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/lock/ @Azure/avm-res-authorization-lock-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/authorization/policy-assignment/ @Azure/avm-res-authorization-policyassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/policy-definition/ @Azure/avm-res-authorization-policydefinition-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/policy-exemption/ @Azure/avm-res-authorization-policyexemption-module-owners-bicep @Azure/avm-core-team-technical-bicep #/avm/res/authorization/policy-set-definition/ @Azure/avm-res-authorization-policysetdefinition-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 681087787b..6c1a8d60bf 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -38,6 +38,7 @@ body: description: Which existing AVM module is this issue related to? options: - "" + - "avm/ptn/authorization/policy-assignment" - "avm/ptn/authorization/role-assignment" # - "avm/ptn/avd-lza/insights" # - "avm/ptn/avd-lza/management-plane" diff --git a/.github/workflows/avm.ptn.authorization.role-assignment.yml b/.github/workflows/avm.ptn.authorization.policy-assignment.yml similarity index 89% rename from .github/workflows/avm.ptn.authorization.role-assignment.yml rename to .github/workflows/avm.ptn.authorization.policy-assignment.yml index c9ad828152..89b9f28b0f 100644 --- a/.github/workflows/avm.ptn.authorization.role-assignment.yml +++ b/.github/workflows/avm.ptn.authorization.policy-assignment.yml @@ -1,4 +1,4 @@ -name: "avm.ptn.authorization.role-assignment" +name: "avm.ptn.authorization.policy-assignment" on: schedule: @@ -26,15 +26,15 @@ on: paths: - ".github/actions/templates/avm-**" - ".github/workflows/avm.template.module.yml" - - ".github/workflows/avm.ptn.authorization.role-assignment.yml" - - "avm/ptn/authorization/role-assignment/**" + - ".github/workflows/avm.ptn.authorization.policy-assignment.yml" + - "avm/ptn/authorization/policy-assignment/**" - "avm/utilities/pipelines/**" - "!avm/utilities/pipelines/platform/**" - "!*/**/README.md" env: - modulePath: "avm/ptn/authorization/role-assignment" - workflowPath: ".github/workflows/avm.ptn.authorization.role-assignment.yml" + modulePath: "avm/ptn/authorization/policy-assignment" + workflowPath: ".github/workflows/avm.ptn.authorization.policy-assignment.yml" concurrency: group: ${{ github.workflow }} diff --git a/avm/ptn/authorization/policy-assignment/README.md b/avm/ptn/authorization/policy-assignment/README.md new file mode 100644 index 0000000000..040d6979dd --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/README.md @@ -0,0 +1,1063 @@ +# Policy Assignments (All scopes) `[Microsoft.Authorization/policyAssignments]` + +This module deploys a Policy Assignment at a Management Group, Subscription or Resource Group scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyAssignments` | [2022-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-06-01/policyAssignments) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/authorization/policy-assignment:`. + +- [Policy Assignments (Management Group scope)](#example-1-policy-assignments-management-group-scope) +- [Policy Assignments (Management Group scope)](#example-2-policy-assignments-management-group-scope) +- [Policy Assignments (Resource Group)](#example-3-policy-assignments-resource-group) +- [Policy Assignments (Resource Group)](#example-4-policy-assignments-resource-group) +- [Policy Assignments (Subscription)](#example-5-policy-assignments-subscription) +- [Policy Assignments (Subscription)](#example-6-policy-assignments-subscription) + +### Example 1: _Policy Assignments (Management Group scope)_ + +This module deploys a Policy Assignment at a Management Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apamgmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + location: '' + metadata: { + assignedBy: 'Bicep' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apamgmin001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep" + } + } + } +} +``` + +
+

+ +### Example 2: _Policy Assignments (Management Group scope)_ + +This module deploys a Policy Assignment at a Management Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apamgmax001' + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + // Non-required parameters + description: '[Description] Policy Assignment at the management group scope' + displayName: '[Display Name] Policy Assignment at the management group scope' + enforcementMode: 'DoNotEnforce' + identity: 'SystemAssigned' + location: '' + managementGroupId: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + overrides: [ + { + kind: 'policyEffect' + selectors: [ + { + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + kind: 'policyDefinitionReferenceId' + } + ] + value: 'Disabled' + } + ] + parameters: { + effect: { + value: 'Disabled' + } + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + } + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + in: [ + 'Microsoft.Compute/virtualMachines' + ] + kind: 'resourceType' + } + { + in: [ + 'westeurope' + ] + kind: 'resourceLocation' + } + ] + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apamgmax001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the management group scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the management group scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "SystemAssigned" + }, + "location": { + "value": "" + }, + "managementGroupId": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/${subscriptionId}/resourceGroups/validation-rg" + ] + }, + "overrides": { + "value": [ + { + "kind": "policyEffect", + "selectors": [ + { + "in": [ + "ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent", + "ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent" + ], + "kind": "policyDefinitionReferenceId" + } + ], + "value": "Disabled" + } + ] + }, + "parameters": { + "value": { + "effect": { + "value": "Disabled" + }, + "enableCollectionOfSqlQueriesForSecurityResearch": { + "value": false + } + } + }, + "resourceSelectors": { + "value": [ + { + "name": "resourceSelector-test", + "selectors": [ + { + "in": [ + "Microsoft.Compute/virtualMachines" + ], + "kind": "resourceType" + }, + { + "in": [ + "westeurope" + ], + "kind": "resourceLocation" + } + ] + } + ] + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + } + } +} +``` + +
+

+ +### Example 3: _Policy Assignments (Resource Group)_ + +This module deploys a Policy Assignment at a Resource Group scope using minimal parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apargmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + location: '' + metadata: { + assignedBy: 'Bicep' + } + resourceGroupName: '' + subscriptionId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apargmin001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep" + } + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _Policy Assignments (Resource Group)_ + +This module deploys a Policy Assignment at a Resource Group scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apargmax001' + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + // Non-required parameters + description: '[Description] Policy Assignment at the resource group scope' + displayName: '[Display Name] Policy Assignment at the resource group scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '' + ] + overrides: [ + { + kind: 'policyEffect' + selectors: [ + { + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + kind: 'policyDefinitionReferenceId' + } + ] + value: 'Disabled' + } + ] + parameters: { + effect: { + value: 'Disabled' + } + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + } + resourceGroupName: '' + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + in: [ + 'Microsoft.Compute/virtualMachines' + ] + kind: 'resourceType' + } + { + in: [ + 'westeurope' + ] + kind: 'resourceLocation' + } + ] + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + subscriptionId: '' + userAssignedIdentityId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apargmax001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the resource group scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the resource group scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "" + ] + }, + "overrides": { + "value": [ + { + "kind": "policyEffect", + "selectors": [ + { + "in": [ + "ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent", + "ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent" + ], + "kind": "policyDefinitionReferenceId" + } + ], + "value": "Disabled" + } + ] + }, + "parameters": { + "value": { + "effect": { + "value": "Disabled" + }, + "enableCollectionOfSqlQueriesForSecurityResearch": { + "value": false + } + } + }, + "resourceGroupName": { + "value": "" + }, + "resourceSelectors": { + "value": [ + { + "name": "resourceSelector-test", + "selectors": [ + { + "in": [ + "Microsoft.Compute/virtualMachines" + ], + "kind": "resourceType" + }, + { + "in": [ + "westeurope" + ], + "kind": "resourceLocation" + } + ] + } + ] + }, + "roleDefinitionIds": { + "value": [ + "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "" + }, + "userAssignedIdentityId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Policy Assignments (Subscription)_ + +This module deploys a Policy Assignment at a Subscription scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apasubmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + location: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + subscriptionId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apasubmin001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d" + }, + // Non-required parameters + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +### Example 6: _Policy Assignments (Subscription)_ + +This module deploys a Policy Assignment at a Subscription scope using common parameters. + + +

+ +via Bicep module + +```bicep +module policyAssignment 'br/public:avm/ptn/authorization/policy-assignment:' = { + name: 'policyAssignmentDeployment' + params: { + // Required parameters + name: 'apasubmax001' + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + // Non-required parameters + description: '[Description] Policy Assignment at the subscription scope' + displayName: '[Display Name] Policy Assignment at the subscription scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: '' + metadata: { + assignedBy: 'Bicep' + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + overrides: [ + { + kind: 'policyEffect' + selectors: [ + { + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + kind: 'policyDefinitionReferenceId' + } + ] + value: 'Disabled' + } + ] + parameters: { + effect: { + value: 'Disabled' + } + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + } + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + in: [ + 'Microsoft.Compute/virtualMachines' + ] + kind: 'resourceType' + } + { + in: [ + 'westeurope' + ] + kind: 'resourceLocation' + } + ] + } + ] + roleDefinitionIds: [ + '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + subscriptionId: '' + userAssignedIdentityId: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "apasubmax001" + }, + "policyDefinitionId": { + "value": "/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611" + }, + // Non-required parameters + "description": { + "value": "[Description] Policy Assignment at the subscription scope" + }, + "displayName": { + "value": "[Display Name] Policy Assignment at the subscription scope" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "" + }, + "metadata": { + "value": { + "assignedBy": "Bicep", + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/${subscriptionId}/resourceGroups/validation-rg" + ] + }, + "overrides": { + "value": [ + { + "kind": "policyEffect", + "selectors": [ + { + "in": [ + "ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent", + "ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent" + ], + "kind": "policyDefinitionReferenceId" + } + ], + "value": "Disabled" + } + ] + }, + "parameters": { + "value": { + "effect": { + "value": "Disabled" + }, + "enableCollectionOfSqlQueriesForSecurityResearch": { + "value": false + } + } + }, + "resourceSelectors": { + "value": [ + { + "name": "resourceSelector-test", + "selectors": [ + { + "in": [ + "Microsoft.Compute/virtualMachines" + ], + "kind": "resourceType" + }, + { + "in": [ + "westeurope" + ], + "kind": "resourceLocation" + } + ] + } + ] + }, + "roleDefinitionIds": { + "value": [ + "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + ] + }, + "subscriptionId": { + "value": "" + }, + "userAssignedIdentityId": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes. | +| [`policyDefinitionId`](#parameter-policydefinitionid) | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-description) | string | This message will be part of response in case of policy violation. | +| [`displayName`](#parameter-displayname) | string | The display name of the policy assignment. Maximum length is 128 characters. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`enforcementMode`](#parameter-enforcementmode) | string | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| [`identity`](#parameter-identity) | string | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`managementGroupId`](#parameter-managementgroupid) | string | The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment. | +| [`metadata`](#parameter-metadata) | object | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| [`nonComplianceMessages`](#parameter-noncompliancemessages) | array | The messages that describe why a resource is non-compliant with the policy. | +| [`notScopes`](#parameter-notscopes) | array | The policy excluded scopes. | +| [`overrides`](#parameter-overrides) | array | The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition. | +| [`parameters`](#parameter-parameters) | object | Parameters for the policy assignment if needed. | +| [`resourceGroupName`](#parameter-resourcegroupname) | string | The Target Scope for the Policy. The name of the resource group for the policy assignment. | +| [`resourceSelectors`](#parameter-resourceselectors) | array | The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location. | +| [`roleDefinitionIds`](#parameter-roledefinitionids) | array | The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. | +| [`subscriptionId`](#parameter-subscriptionid) | string | The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. | +| [`userAssignedIdentityId`](#parameter-userassignedidentityid) | string | The Resource ID for the user assigned identity to assign to the policy assignment. | + +### Parameter: `name` + +Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes. + +- Required: Yes +- Type: string + +### Parameter: `policyDefinitionId` + +Specifies the ID of the policy definition or policy set definition being assigned. + +- Required: Yes +- Type: string + +### Parameter: `description` + +This message will be part of response in case of policy violation. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `displayName` + +The display name of the policy assignment. Maximum length is 128 characters. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `enforcementMode` + +The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. + +- Required: No +- Type: string +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'DoNotEnforce' + ] + ``` + +### Parameter: `identity` + +The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. + +- Required: No +- Type: string +- Default: `'SystemAssigned'` +- Allowed: + ```Bicep + [ + 'None' + 'SystemAssigned' + 'UserAssigned' + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +### Parameter: `managementGroupId` + +The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment. + +- Required: No +- Type: string +- Default: `[managementGroup().name]` + +### Parameter: `metadata` + +The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `nonComplianceMessages` + +The messages that describe why a resource is non-compliant with the policy. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `notScopes` + +The policy excluded scopes. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `overrides` + +The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `parameters` + +Parameters for the policy assignment if needed. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `resourceGroupName` + +The Target Scope for the Policy. The name of the resource group for the policy assignment. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `resourceSelectors` + +The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `roleDefinitionIds` + +The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `subscriptionId` + +The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `userAssignedIdentityId` + +The Resource ID for the user assigned identity to assign to the policy assignment. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceId` | string | Policy Assignment resource ID. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/authorization/policy-assignment/main.bicep b/avm/ptn/authorization/policy-assignment/main.bicep new file mode 100644 index 0000000000..286669834a --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/main.bicep @@ -0,0 +1,172 @@ +metadata name = 'Policy Assignments (All scopes)' +metadata description = 'This module deploys a Policy Assignment at a Management Group, Subscription or Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes.') +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment.') +param subscriptionId string = '' + +@sys.description('Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment.') +param resourceGroupName string = '' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +@sys.description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: take('46d3xbcp.ptn.authorization-policyassignment.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}', 64) + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +module policyAssignment_mg 'modules/management-group.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + managementGroupId: managementGroupId + location: location + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } +} + +module policyAssignment_sub 'modules/subscription.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + subscriptionId: subscriptionId + location: location + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } +} + +module policyAssignment_rg 'modules/resource-group.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyAssignment-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: name + policyDefinitionId: policyDefinitionId + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + parameters: !empty(parameters) ? parameters : {} + identity: identity + userAssignedIdentityId: userAssignedIdentityId + roleDefinitionIds: !empty(roleDefinitionIds) ? roleDefinitionIds : [] + metadata: !empty(metadata) ? metadata : {} + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + subscriptionId: subscriptionId + location: location + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } +} + +@sys.description('Policy Assignment Name.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.name : policyAssignment_rg.outputs.name) + +@sys.description('Policy Assignment principal ID.') +output principalId string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.principalId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.principalId : policyAssignment_rg.outputs.principalId) + +@sys.description('Policy Assignment resource ID.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.resourceId : policyAssignment_rg.outputs.resourceId) + +@sys.description('The location the resource was deployed into.') +output location string = empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_mg.outputs.location : (!empty(subscriptionId) && empty(resourceGroupName) ? policyAssignment_sub.outputs.location : policyAssignment_rg.outputs.location) diff --git a/avm/ptn/authorization/policy-assignment/main.json b/avm/ptn/authorization/policy-assignment/main.json new file mode 100644 index 0000000000..97fcaefd0c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/main.json @@ -0,0 +1,989 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "3274497359181095240" + }, + "name": "Policy Assignments (All scopes)", + "description": "This module deploys a Policy Assignment at a Management Group, Subscription or Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope, 64 characters for subscription and resource group scopes." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[take(format('46d3xbcp.ptn.authorization-policyassignment.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", + "location": "[parameters('location')]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + { + "condition": "[and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))]", + "scope": "[format('Microsoft.Management/managementGroups/{0}', parameters('managementGroupId'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "policyDefinitionId": { + "value": "[parameters('policyDefinitionId')]" + }, + "displayName": "[if(not(empty(parameters('displayName'))), createObject('value', parameters('displayName')), createObject('value', ''))]", + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "parameters": "[if(not(empty(parameters('parameters'))), createObject('value', parameters('parameters')), createObject('value', createObject()))]", + "identity": { + "value": "[parameters('identity')]" + }, + "userAssignedIdentityId": { + "value": "[parameters('userAssignedIdentityId')]" + }, + "roleDefinitionIds": "[if(not(empty(parameters('roleDefinitionIds'))), createObject('value', parameters('roleDefinitionIds')), createObject('value', createArray()))]", + "metadata": "[if(not(empty(parameters('metadata'))), createObject('value', parameters('metadata')), createObject('value', createObject()))]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), createObject('value', parameters('nonComplianceMessages')), createObject('value', createArray()))]", + "enforcementMode": { + "value": "[parameters('enforcementMode')]" + }, + "notScopes": "[if(not(empty(parameters('notScopes'))), createObject('value', parameters('notScopes')), createObject('value', createArray()))]", + "managementGroupId": { + "value": "[parameters('managementGroupId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "overrides": "[if(not(empty(parameters('overrides'))), createObject('value', parameters('overrides')), createObject('value', createArray()))]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), createObject('value', parameters('resourceSelectors')), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15016174258473942920" + }, + "name": "Policy Assignments (Management Group scope)", + "description": "This module deploys a Policy Assignment at a Management Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "managementGroupId": { + "type": "string", + "defaultValue": "[managementGroup().name]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + } + }, + "variables": { + "identityVar": "[if(equals(parameters('identity'), 'SystemAssigned'), createObject('type', parameters('identity')), if(equals(parameters('identity'), 'UserAssigned'), createObject('type', parameters('identity'), 'userAssignedIdentities', createObject(format('{0}', parameters('userAssignedIdentityId')), createObject())), null()))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "displayName": "[if(not(empty(parameters('displayName'))), parameters('displayName'), null())]", + "metadata": "[if(not(empty(parameters('metadata'))), parameters('metadata'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[parameters('parameters')]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), parameters('nonComplianceMessages'), createArray())]", + "enforcementMode": "[parameters('enforcementMode')]", + "notScopes": "[if(not(empty(parameters('notScopes'))), parameters('notScopes'), createArray())]", + "overrides": "[if(not(empty(parameters('overrides'))), parameters('overrides'), createArray())]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), parameters('resourceSelectors'), createArray())]" + }, + "identity": "[variables('identityVar')]" + }, + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleDefinitionIds'))]" + }, + "condition": "[and(not(empty(parameters('roleDefinitionIds'))), equals(parameters('identity'), 'SystemAssigned'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('managementGroupId'), parameters('roleDefinitionIds')[copyIndex()], parameters('location'), parameters('name'))]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]", + "principalId": "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[parameters('name')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[coalesce(tryGet(tryGet(reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').location]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "policyDefinitionId": { + "value": "[parameters('policyDefinitionId')]" + }, + "displayName": "[if(not(empty(parameters('displayName'))), createObject('value', parameters('displayName')), createObject('value', ''))]", + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "parameters": "[if(not(empty(parameters('parameters'))), createObject('value', parameters('parameters')), createObject('value', createObject()))]", + "identity": { + "value": "[parameters('identity')]" + }, + "userAssignedIdentityId": { + "value": "[parameters('userAssignedIdentityId')]" + }, + "roleDefinitionIds": "[if(not(empty(parameters('roleDefinitionIds'))), createObject('value', parameters('roleDefinitionIds')), createObject('value', createArray()))]", + "metadata": "[if(not(empty(parameters('metadata'))), createObject('value', parameters('metadata')), createObject('value', createObject()))]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), createObject('value', parameters('nonComplianceMessages')), createObject('value', createArray()))]", + "enforcementMode": { + "value": "[parameters('enforcementMode')]" + }, + "notScopes": "[if(not(empty(parameters('notScopes'))), createObject('value', parameters('notScopes')), createObject('value', createArray()))]", + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "overrides": "[if(not(empty(parameters('overrides'))), createObject('value', parameters('overrides')), createObject('value', createArray()))]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), createObject('value', parameters('resourceSelectors')), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "977404313320239280" + }, + "name": "Policy Assignments (Subscription scope)", + "description": "This module deploys a Policy Assignment at a Subscription scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment." + } + } + }, + "variables": { + "identityVar": "[if(equals(parameters('identity'), 'SystemAssigned'), createObject('type', parameters('identity')), if(equals(parameters('identity'), 'UserAssigned'), createObject('type', parameters('identity'), 'userAssignedIdentities', createObject(format('{0}', parameters('userAssignedIdentityId')), createObject())), null()))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "displayName": "[if(not(empty(parameters('displayName'))), parameters('displayName'), null())]", + "metadata": "[if(not(empty(parameters('metadata'))), parameters('metadata'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[parameters('parameters')]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), parameters('nonComplianceMessages'), createArray())]", + "enforcementMode": "[parameters('enforcementMode')]", + "notScopes": "[if(not(empty(parameters('notScopes'))), parameters('notScopes'), createArray())]", + "overrides": "[if(not(empty(parameters('overrides'))), parameters('overrides'), createArray())]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), parameters('resourceSelectors'), createArray())]" + }, + "identity": "[variables('identityVar')]" + }, + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleDefinitionIds'))]" + }, + "condition": "[and(not(empty(parameters('roleDefinitionIds'))), equals(parameters('identity'), 'SystemAssigned'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), parameters('roleDefinitionIds')[copyIndex()], parameters('location'), parameters('name'))]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]", + "principalId": "[reference(subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[parameters('name')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[coalesce(tryGet(tryGet(reference(subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').location]" + } + } + } + } + }, + { + "condition": "[and(not(empty(parameters('resourceGroupName'))), not(empty(parameters('subscriptionId'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "policyDefinitionId": { + "value": "[parameters('policyDefinitionId')]" + }, + "displayName": "[if(not(empty(parameters('displayName'))), createObject('value', parameters('displayName')), createObject('value', ''))]", + "description": "[if(not(empty(parameters('description'))), createObject('value', parameters('description')), createObject('value', ''))]", + "parameters": "[if(not(empty(parameters('parameters'))), createObject('value', parameters('parameters')), createObject('value', createObject()))]", + "identity": { + "value": "[parameters('identity')]" + }, + "userAssignedIdentityId": { + "value": "[parameters('userAssignedIdentityId')]" + }, + "roleDefinitionIds": "[if(not(empty(parameters('roleDefinitionIds'))), createObject('value', parameters('roleDefinitionIds')), createObject('value', createArray()))]", + "metadata": "[if(not(empty(parameters('metadata'))), createObject('value', parameters('metadata')), createObject('value', createObject()))]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), createObject('value', parameters('nonComplianceMessages')), createObject('value', createArray()))]", + "enforcementMode": { + "value": "[parameters('enforcementMode')]" + }, + "notScopes": "[if(not(empty(parameters('notScopes'))), createObject('value', parameters('notScopes')), createObject('value', createArray()))]", + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "overrides": "[if(not(empty(parameters('overrides'))), createObject('value', parameters('overrides')), createObject('value', createArray()))]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), createObject('value', parameters('resourceSelectors')), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "17106923564853756802" + }, + "name": "Policy Assignments (Resource Group scope)", + "description": "This module deploys a Policy Assignment at a Resource Group scope.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Required. Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. This message will be part of response in case of policy violation." + } + }, + "displayName": { + "type": "string", + "defaultValue": "", + "maxLength": 128, + "metadata": { + "description": "Optional. The display name of the policy assignment. Maximum length is 128 characters." + } + }, + "policyDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the ID of the policy definition or policy set definition being assigned." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters for the policy assignment if needed." + } + }, + "identity": { + "type": "string", + "defaultValue": "SystemAssigned", + "allowedValues": [ + "SystemAssigned", + "UserAssigned", + "None" + ], + "metadata": { + "description": "Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions." + } + }, + "userAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource ID for the user assigned identity to assign to the policy assignment." + } + }, + "roleDefinitionIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs." + } + }, + "nonComplianceMessages": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The messages that describe why a resource is non-compliant with the policy." + } + }, + "enforcementMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "DoNotEnforce" + ], + "metadata": { + "description": "Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce." + } + }, + "notScopes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy excluded scopes." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "overrides": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition." + } + }, + "resourceSelectors": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location." + } + }, + "subscriptionId": { + "type": "string", + "defaultValue": "[subscription().subscriptionId]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment. If not provided, will use the current scope for deployment." + } + } + }, + "variables": { + "identityVar": "[if(equals(parameters('identity'), 'SystemAssigned'), createObject('type', parameters('identity')), if(equals(parameters('identity'), 'UserAssigned'), createObject('type', parameters('identity'), 'userAssignedIdentities', createObject(format('{0}', parameters('userAssignedIdentityId')), createObject())), null()))]" + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "displayName": "[if(not(empty(parameters('displayName'))), parameters('displayName'), null())]", + "metadata": "[if(not(empty(parameters('metadata'))), parameters('metadata'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[parameters('parameters')]", + "nonComplianceMessages": "[if(not(empty(parameters('nonComplianceMessages'))), parameters('nonComplianceMessages'), createArray())]", + "enforcementMode": "[parameters('enforcementMode')]", + "notScopes": "[if(not(empty(parameters('notScopes'))), parameters('notScopes'), createArray())]", + "overrides": "[if(not(empty(parameters('overrides'))), parameters('overrides'), createArray())]", + "resourceSelectors": "[if(not(empty(parameters('resourceSelectors'))), parameters('resourceSelectors'), createArray())]" + }, + "identity": "[variables('identityVar')]" + }, + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('roleDefinitionIds'))]" + }, + "condition": "[and(not(empty(parameters('roleDefinitionIds'))), equals(parameters('identity'), 'SystemAssigned'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('subscriptionId'), parameters('resourceGroupName'), parameters('roleDefinitionIds')[copyIndex()], parameters('location'), parameters('name'))]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionIds')[copyIndex()]]", + "principalId": "[reference(resourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[parameters('name')]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[coalesce(tryGet(tryGet(reference(resourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[resourceId('Microsoft.Authorization/policyAssignments', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the policy was assigned to." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Authorization/policyAssignments', parameters('name')), '2022-06-01', 'full').location]" + } + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "Policy Assignment Name." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.name.value))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Policy Assignment principal ID." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.principalId.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.principalId.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.principalId.value))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Policy Assignment resource ID." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.resourceId.value))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[if(and(empty(parameters('subscriptionId')), empty(parameters('resourceGroupName'))), reference(extensionResourceId(tenantResourceId('Microsoft.Management/managementGroups', parameters('managementGroupId')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-MG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.location.value, if(and(not(empty(parameters('subscriptionId'))), empty(parameters('resourceGroupName'))), reference(subscriptionResourceId(parameters('subscriptionId'), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-Sub-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.location.value, reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('{0}-PolicyAssignment-RG-Module', uniqueString(deployment().name, parameters('location')))), '2022-09-01').outputs.location.value))]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/authorization/policy-assignment/modules/management-group.bicep b/avm/ptn/authorization/policy-assignment/modules/management-group.bicep new file mode 100644 index 0000000000..721899031a --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/modules/management-group.bicep @@ -0,0 +1,112 @@ +metadata name = 'Policy Assignments (Management Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Management Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope.') +@maxLength(24) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The Target Scope for the Policy. The name of the management group for the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } + identity: identityVar +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(managementGroupId, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = policyAssignment.?identity.?principalId ?? '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = policyAssignment.id + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/avm/ptn/authorization/policy-assignment/modules/resource-group.bicep b/avm/ptn/authorization/policy-assignment/modules/resource-group.bicep new file mode 100644 index 0000000000..8850bf099c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/modules/resource-group.bicep @@ -0,0 +1,119 @@ +metadata name = 'Policy Assignments (Resource Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Resource Group scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'resourceGroup' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The Target Scope for the Policy. The name of the resource group for the policy assignment. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } + identity: identityVar +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(subscriptionId, resourceGroupName, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = policyAssignment.?identity.?principalId ?? '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = policyAssignment.id + +@sys.description('The name of the resource group the policy was assigned to.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/avm/ptn/authorization/policy-assignment/modules/subscription.bicep b/avm/ptn/authorization/policy-assignment/modules/subscription.bicep new file mode 100644 index 0000000000..1740e32987 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/modules/subscription.bicep @@ -0,0 +1,112 @@ +metadata name = 'Policy Assignments (Subscription scope)' +metadata description = 'This module deploys a Policy Assignment at a Subscription scope.' +metadata owner = 'Azure/module-maintainers' + +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. This message will be part of response in case of policy violation.') +param description string = '' + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Required. Specifies the ID of the policy definition or policy set definition being assigned.') +param policyDefinitionId string + +@sys.description('Optional. Parameters for the policy assignment if needed.') +param parameters object = {} + +@sys.description('Optional. The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning \'Modify\' policy definitions.') +@allowed([ + 'SystemAssigned' + 'UserAssigned' + 'None' +]) +param identity string = 'SystemAssigned' + +@sys.description('Optional. The Resource ID for the user assigned identity to assign to the policy assignment.') +param userAssignedIdentityId string = '' + +@sys.description('Optional. The IDs Of the Azure Role Definition list that is used to assign permissions to the identity. You need to provide either the fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles for the list IDs for built-in Roles. They must match on what is on the policy definition.') +param roleDefinitionIds array = [] + +@sys.description('Optional. The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The messages that describe why a resource is non-compliant with the policy.') +param nonComplianceMessages array = [] + +@sys.description('Optional. The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce.') +@allowed([ + 'Default' + 'DoNotEnforce' +]) +param enforcementMode string = 'Default' + +@sys.description('Optional. The policy excluded scopes.') +param notScopes array = [] + +@sys.description('Optional. Location for all resources.') +param location string = deployment().location + +@sys.description('Optional. The policy property value override. Allows changing the effect of a policy definition without modifying the underlying policy definition or using a parameterized effect in the policy definition.') +param overrides array = [] + +@sys.description('Optional. The resource selector list to filter policies by resource properties. Facilitates safe deployment practices (SDP) by enabling gradual roll out policy assignments based on factors like resource location, resource type, or whether a resource has a location.') +param resourceSelectors array = [] + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = { + name: name + location: location + properties: { + displayName: !empty(displayName) ? displayName : null + metadata: !empty(metadata) ? metadata : null + description: !empty(description) ? description : null + policyDefinitionId: policyDefinitionId + parameters: parameters + nonComplianceMessages: !empty(nonComplianceMessages) ? nonComplianceMessages : [] + enforcementMode: enforcementMode + notScopes: !empty(notScopes) ? notScopes : [] + overrides: !empty(overrides) ? overrides : [] + resourceSelectors: !empty(resourceSelectors) ? resourceSelectors : [] + } + identity: identityVar +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for roleDefinitionId in roleDefinitionIds: if (!empty(roleDefinitionIds) && identity == 'SystemAssigned') { + name: guid(subscriptionId, roleDefinitionId, location, name) + properties: { + roleDefinitionId: roleDefinitionId + principalId: policyAssignment.identity.principalId + principalType: 'ServicePrincipal' + } +}] + +@sys.description('Policy Assignment Name.') +output name string = policyAssignment.name + +@sys.description('Policy Assignment principal ID.') +output principalId string = policyAssignment.?identity.?principalId ?? '' + +@sys.description('Policy Assignment resource ID.') +output resourceId string = policyAssignment.id + +@sys.description('The location the resource was deployed into.') +output location string = policyAssignment.location diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep new file mode 100644 index 0000000000..2c043ce041 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.defaults/main.test.bicep @@ -0,0 +1,33 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Management Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Management Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apamgmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Audit VMs that do not use managed disks + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + metadata: { + assignedBy: 'Bicep' + } + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep new file mode 100644 index 0000000000..07d5b2309a --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/mg.max/main.test.bicep @@ -0,0 +1,96 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Management Group scope)' +metadata description = 'This module deploys a Policy Assignment at a Management Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apamgmax' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = '#_subscriptionId_#' + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Configure Azure Defender for SQL agents on virtual machines + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + description: '[Description] Policy Assignment at the management group scope' + displayName: '[Display Name] Policy Assignment at the management group scope' + enforcementMode: 'DoNotEnforce' + identity: 'SystemAssigned' + location: resourceLocation + managementGroupId: last(split(managementGroup().id, '/')) + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' // Contributor role + ] + overrides: [ + { + kind: 'policyEffect' + value: 'Disabled' + selectors: [ + { + kind: 'policyDefinitionReferenceId' + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + } + ] + } + ] + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + kind: 'resourceType' + in: [ + 'Microsoft.Compute/virtualMachines' + ] + } + { + kind: 'resourceLocation' + in: [ + 'westeurope' + ] + } + ] + } + ] + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep new file mode 100644 index 0000000000..ff1eaeb6a7 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.defaults/main.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Resource Group)' +metadata description = 'This module deploys a Policy Assignment at a Resource Group scope using minimal parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.policyassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apargmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Audit VMs that do not use managed disks + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + metadata: { + assignedBy: 'Bicep' + } + location: resourceLocation + resourceGroupName: resourceGroupDeploy.outputs.name + subscriptionId: subscriptionId + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep new file mode 100644 index 0000000000..f4151d61c7 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/dependencies.bicep @@ -0,0 +1,33 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + accessPolicies: [] + } +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep new file mode 100644 index 0000000000..9dabc3b7a1 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/rg.max/main.test.bicep @@ -0,0 +1,127 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Resource Group)' +metadata description = 'This module deploys a Policy Assignment at a Resource Group scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.policyassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apargmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@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.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroupDeploy 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup(subscriptionId, resourceGroupName) + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Configure Azure Defender for SQL agents on virtual machines + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + description: '[Description] Policy Assignment at the resource group scope' + displayName: '[Display Name] Policy Assignment at the resource group scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: resourceLocation + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + nestedDependencies.outputs.keyVaultResourceId + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + resourceGroupName: resourceGroupDeploy.outputs.name + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + overrides: [ + { + kind: 'policyEffect' + value: 'Disabled' + selectors: [ + { + kind: 'policyDefinitionReferenceId' + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + } + ] + } + ] + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + kind: 'resourceType' + in: [ + 'Microsoft.Compute/virtualMachines' + ] + } + { + kind: 'resourceLocation' + in: [ + 'westeurope' + ] + } + ] + } + ] + subscriptionId: subscriptionId + userAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep new file mode 100644 index 0000000000..1cd7910296 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.defaults/main.test.bicep @@ -0,0 +1,39 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Subscription)' +metadata description = 'This module deploys a Policy Assignment at a Subscription scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apasubmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = '#_subscriptionId_#' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Audit VMs that do not use managed disks + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + subscriptionId: subscriptionId + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + location: resourceLocation + } +} diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep new file mode 100644 index 0000000000..71b8f3cee1 --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep new file mode 100644 index 0000000000..119660120c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/interim.dependencies.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Required. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: managedIdentityName + location: location + } +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = nestedDependencies.outputs.managedIdentityResourceId diff --git a/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep new file mode 100644 index 0000000000..0d90ce4c8c --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/tests/e2e/sub.max/main.test.bicep @@ -0,0 +1,126 @@ +targetScope = 'managementGroup' +metadata name = 'Policy Assignments (Subscription)' +metadata description = 'This module deploys a Policy Assignment at a Subscription scope using common parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-authorization.policyassignments-${serviceShort}-rg' + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apasubmax' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@sys.description('Optional. The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. If not provided, will use the current scope for deployment.') +param subscriptionId string = '#_subscriptionId_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module resourceGroup 'br/public:avm/res/resources/resource-group:0.2.3' ={ + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-resourceGroup' + params: { + name: resourceGroupName + location: resourceLocation + } +} + +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('${subscriptionId}') + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '${namePrefix}${serviceShort}001' + //Configure Azure Defender for SQL agents on virtual machines + policyDefinitionId: '/providers/Microsoft.Authorization/policySetDefinitions/39a366e6-fdde-4f41-bbf8-3757f46d1611' + description: '[Description] Policy Assignment at the subscription scope' + displayName: '[Display Name] Policy Assignment at the subscription scope' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: resourceLocation + metadata: { + category: 'Security' + version: '1.0' + assignedBy: 'Bicep' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/${subscriptionId}/resourceGroups/validation-rg' + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + roleDefinitionIds: [ + '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + ] + overrides: [ + { + kind: 'policyEffect' + value: 'Disabled' + selectors: [ + { + kind: 'policyDefinitionReferenceId' + in: [ + 'ASC_DeployAzureDefenderForSqlAdvancedThreatProtectionWindowsAgent' + 'ASC_DeployAzureDefenderForSqlVulnerabilityAssessmentWindowsAgent' + ] + } + ] + } + ] + resourceSelectors: [ + { + name: 'resourceSelector-test' + selectors: [ + { + kind: 'resourceType' + in: [ + 'Microsoft.Compute/virtualMachines' + ] + } + { + kind: 'resourceLocation' + in: [ + 'westeurope' + ] + } + ] + } + ] + subscriptionId: subscriptionId + userAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + } +} diff --git a/avm/ptn/authorization/policy-assignment/version.json b/avm/ptn/authorization/policy-assignment/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/authorization/policy-assignment/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 77b523b1f592ca3b27ef51411dbe883309afd813 Mon Sep 17 00:00:00 2001 From: Buddy <38195643+tsc-buddy@users.noreply.github.com> Date: Sat, 20 Apr 2024 03:39:53 +1200 Subject: [PATCH 56/66] fix: 1507 linux asp (#1711) ## Description Fixes [issue 1507](https://github.com/Azure/bicep-registry-modules/issues/1507) logged relating to Linux ASP selection and the reserved property. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.serverfarm](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=fix%2F1507-linux-asp)](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/web/serverfarm/README.md | 26 +++++++++---------- avm/res/web/serverfarm/main.bicep | 2 +- avm/res/web/serverfarm/main.json | 6 ++--- .../serverfarm/tests/e2e/max/main.test.bicep | 12 ++++----- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 7788f77636..1a3e585904 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -112,11 +112,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { // Required parameters name: 'wsfmax001' sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' + capacity: 1 + family: 'S' + name: 'S1' + size: 'S1' + tier: 'Standard' } // Non-required parameters diagnosticSettings: [ @@ -162,7 +162,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: true + zoneRedundant: false } } ``` @@ -185,11 +185,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { }, "sku": { "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" + "capacity": 1, + "family": "S", + "name": "S1", + "size": "S1", + "tier": "Standard" } }, // Non-required parameters @@ -251,7 +251,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { } }, "zoneRedundant": { - "value": true + "value": false } } } @@ -439,7 +439,7 @@ Defaults to false when creating Windows/app App Service Plan. Required if creati - Required: No - Type: bool -- Default: `False` +- Default: `[equals(parameters('kind'), 'Linux')]` ### Parameter: `appServiceEnvironmentId` diff --git a/avm/res/web/serverfarm/main.bicep b/avm/res/web/serverfarm/main.bicep index 7cff682e16..5deb3fe099 100644 --- a/avm/res/web/serverfarm/main.bicep +++ b/avm/res/web/serverfarm/main.bicep @@ -35,7 +35,7 @@ param location string = resourceGroup().location param kind string = 'App' @description('Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true.') -param reserved bool = false +param reserved bool = (kind == 'Linux') @description('Optional. The Resource ID of the App Service Environment to use for the App Service Plan.') param appServiceEnvironmentId string = '' diff --git a/avm/res/web/serverfarm/main.json b/avm/res/web/serverfarm/main.json index bf40400bed..68999ed0f2 100644 --- a/avm/res/web/serverfarm/main.json +++ b/avm/res/web/serverfarm/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12495804892051718765" + "version": "0.26.170.59819", + "templateHash": "16669238654401736455" }, "name": "App Service Plan", "description": "This module deploys an App Service Plan.", @@ -231,7 +231,7 @@ }, "reserved": { "type": "bool", - "defaultValue": false, + "defaultValue": "[equals(parameters('kind'), 'Linux')]", "metadata": { "description": "Conditional. Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true." } diff --git a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep index 5451356bd1..f7e0fb3a88 100644 --- a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep @@ -68,14 +68,14 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' location: tempLocation sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 + name: 'S1' + tier: 'Standard' + size: 'S1' + family: 'S' + capacity: 1 } perSiteScaling: true - zoneRedundant: true + zoneRedundant: false kind: 'App' lock: { name: 'lock' From cc66c2b41a9438dbaed78aa4501124f775029a36 Mon Sep 17 00:00:00 2001 From: Ilhaan Rasheed Date: Fri, 19 Apr 2024 08:41:25 -0700 Subject: [PATCH 57/66] feat: `avm/res/network/application-gateway` (#835) New module for Application Gateway migrated from CARML [![avm.res.network.application-gateway](https://github.com/ilhaan/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml/badge.svg?branch=avm-application-gateway)](https://github.com/ilhaan/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml) --------- Co-authored-by: Alexander Sehr Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.network.application-gateway.yml | 86 + avm/res/network/application-gateway/README.md | 3257 +++++++++++++++++ .../network/application-gateway/main.bicep | 616 ++++ avm/res/network/application-gateway/main.json | 1633 +++++++++ .../tests/e2e/defaults/dependencies.bicep | 60 + .../tests/e2e/defaults/main.test.bicep | 146 + .../tests/e2e/max/dependencies.bicep | 154 + .../tests/e2e/max/main.test.bicep | 524 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 154 + .../tests/e2e/waf-aligned/main.test.bicep | 486 +++ .../network/application-gateway/version.json | 7 + 13 files changed, 7125 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.network.application-gateway.yml create mode 100644 avm/res/network/application-gateway/README.md create mode 100644 avm/res/network/application-gateway/main.bicep create mode 100644 avm/res/network/application-gateway/main.json create mode 100644 avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/max/main.test.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/network/application-gateway/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2851e9058b..e04de640ac 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -78,7 +78,7 @@ #/avm/res/managed-services/registration-definition/ @Azure/avm-res-managedservices-registrationdefinition-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/management/management-group/ @Azure/avm-res-management-managementgroup-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/net-app/net-app-account/ @Azure/avm-res-netapp-netappaccount-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/network/application-gateway/ @Azure/avm-res-network-applicationgateway-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/network/application-gateway/ @Azure/avm-res-network-applicationgateway-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/application-gateway-web-application-firewall-policy/ @Azure/avm-res-network-applicationgatewaywebapplicationfirewallpolicy-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/application-security-group/ @Azure/avm-res-network-applicationsecuritygroup-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/network/azure-firewall/ @Azure/avm-res-network-azurefirewall-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 6c1a8d60bf..40c763986f 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -111,7 +111,7 @@ body: # - "avm/res/managed-services/registration-definition" - "avm/res/management/management-group" - "avm/res/net-app/net-app-account" - # - "avm/res/network/application-gateway" + - "avm/res/network/application-gateway" - "avm/res/network/application-gateway-web-application-firewall-policy" - "avm/res/network/application-security-group" - "avm/res/network/azure-firewall" diff --git a/.github/workflows/avm.res.network.application-gateway.yml b/.github/workflows/avm.res.network.application-gateway.yml new file mode 100644 index 0000000000..e55979ee5e --- /dev/null +++ b/.github/workflows/avm.res.network.application-gateway.yml @@ -0,0 +1,86 @@ +name: "avm.res.network.application-gateway" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + - avm-application-gateway + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.network.application-gateway.yml" + - "avm/res/network/application-gateway/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/network/application-gateway" + workflowPath: ".github/workflows/avm.res.network.application-gateway.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/network/application-gateway/README.md b/avm/res/network/application-gateway/README.md new file mode 100644 index 0000000000..f3faed0358 --- /dev/null +++ b/avm/res/network/application-gateway/README.md @@ -0,0 +1,3257 @@ +# Network Application Gateways `[Microsoft.Network/applicationGateways]` + +This module deploys a Network Application Gateway. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/applicationGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/applicationGateways) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/application-gateway:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module applicationGateway 'br/public:avm/res/network/application-gateway:' = { + name: 'applicationGatewayDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + ] + backendHttpSettingsCollection: [ + { + name: 'backendHttpSettings1' + properties: { + cookieBasedAffinity: 'Disabled' + port: 80 + protocol: 'Http' + } + } + ] + frontendIPConfigurations: [ + { + name: 'frontendIPConfig1' + properties: { + publicIPAddress: { + id: '' + } + } + } + ] + frontendPorts: [ + { + name: 'frontendPort1' + properties: { + port: 80 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'publicIPConfig1' + properties: { + subnet: { + id: '' + } + } + } + ] + httpListeners: [ + { + name: 'httpListener1' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostName: 'www.contoso.com' + protocol: 'Http' + } + } + ] + location: '' + requestRoutingRules: [ + { + name: 'requestRoutingRule1' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 100 + ruleType: 'Basic' + } + } + ] + webApplicationFirewallConfiguration: { + enabled: false + } + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "backendAddressPool1" + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "backendHttpSettings1", + "properties": { + "cookieBasedAffinity": "Disabled", + "port": 80, + "protocol": "Http" + } + } + ] + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "frontendIPConfig1", + "properties": { + "publicIPAddress": { + "id": "" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "frontendPort1", + "properties": { + "port": 80 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "httpListener1", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostName": "www.contoso.com", + "protocol": "Http" + } + } + ] + }, + "location": { + "value": "" + }, + "requestRoutingRules": { + "value": [ + { + "name": "requestRoutingRule1", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 100, + "ruleType": "Basic" + } + } + ] + }, + "webApplicationFirewallConfiguration": { + "value": { + "enabled": false + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

+ +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module applicationGateway 'br/public:avm/res/network/application-gateway:' = { + name: 'applicationGatewayDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + enableHttp2: true + enableTelemetry: '' + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: '' + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + privateLinkConfiguration: { + id: '' + } + publicIPAddress: { + id: '' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: '' + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'public' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + privateLinkConfigurations: [ + { + id: '' + name: 'pvtlink01' + properties: { + ipConfigurations: [ + { + id: '' + name: 'privateLinkIpConfig1' + properties: { + primary: false + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '' + } + } + } + ] + } + } + ] + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '' + } + priority: 300 + redirectConfiguration: { + id: '' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '' + } + priority: 350 + redirectConfiguration: { + id: '' + } + rewriteRuleSet: { + id: '' + } + ruleType: 'Basic' + } + } + ] + rewriteRuleSets: [ + { + id: '' + name: 'customRewrite' + properties: { + rewriteRules: [ + { + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + conditions: [] + name: 'NewRewrite' + ruleSequence: 100 + } + ] + } + } + ] + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: 'az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: '' + } + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + webApplicationFirewallConfiguration: { + disabledRuleGroups: [ + { + ruleGroupName: 'Known-CVEs' + } + { + ruleGroupName: 'REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION' + } + { + ruleGroupName: 'REQUEST-941-APPLICATION-ATTACK-XSS' + } + ] + enabled: true + exclusions: [ + { + matchVariable: 'RequestHeaderNames' + selector: 'hola' + selectorMatchOperator: 'StartsWith' + } + ] + fileUploadLimitInMb: 100 + firewallMode: 'Detection' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "appServiceBackendPool", + "properties": { + "backendAddresses": [ + { + "fqdn": "aghapp.azurewebsites.net" + } + ] + } + }, + { + "name": "privateVmBackendPool", + "properties": { + "backendAddresses": [ + { + "ipAddress": "10.0.0.4" + } + ] + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "appServiceBackendHttpsSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "port": 443, + "protocol": "Https", + "requestTimeout": 30 + } + }, + { + "name": "privateVmHttpSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "port": 80, + "probe": { + "id": "" + }, + "protocol": "Http", + "requestTimeout": 30 + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "enableHttp2": { + "value": true + }, + "enableTelemetry": { + "value": "" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.0.20", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "privateLinkConfiguration": { + "id": "" + }, + "publicIPAddress": { + "id": "" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "port443", + "properties": { + "port": 443 + } + }, + { + "name": "port4433", + "properties": { + "port": 4433 + } + }, + { + "name": "port80", + "properties": { + "port": 80 + } + }, + { + "name": "port8080", + "properties": { + "port": 8080 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "apw-ip-configuration", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "public443", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "private4433", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "httpRedirect80", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect8080", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "public", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, + "privateLinkConfigurations": { + "value": [ + { + "id": "", + "name": "pvtlink01", + "properties": { + "ipConfigurations": [ + { + "id": "", + "name": "privateLinkIpConfig1", + "properties": { + "primary": false, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "" + } + } + } + ] + } + } + ] + }, + "probes": { + "value": [ + { + "name": "privateVmHttpSettingProbe", + "properties": { + "host": "10.0.0.4", + "interval": 60, + "match": { + "statusCodes": [ + "200", + "401" + ] + }, + "minServers": 3, + "path": "/", + "pickHostNameFromBackendHttpSettings": false, + "protocol": "Http", + "timeout": 15, + "unhealthyThreshold": 5 + } + } + ] + }, + "redirectConfigurations": { + "value": [ + { + "name": "httpRedirect80", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + }, + { + "name": "httpRedirect8080", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + } + ] + }, + "requestRoutingRules": { + "value": [ + { + "name": "public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 200, + "ruleType": "Basic" + } + }, + { + "name": "private4433-privateVmHttpSetting-privateVmHttpSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 250, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect80-public443", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 300, + "redirectConfiguration": { + "id": "" + }, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect8080-private4433", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 350, + "redirectConfiguration": { + "id": "" + }, + "rewriteRuleSet": { + "id": "" + }, + "ruleType": "Basic" + } + } + ] + }, + "rewriteRuleSets": { + "value": [ + { + "id": "", + "name": "customRewrite", + "properties": { + "rewriteRules": [ + { + "actionSet": { + "requestHeaderConfigurations": [ + { + "headerName": "Content-Type", + "headerValue": "JSON" + }, + { + "headerName": "someheader" + } + ], + "responseHeaderConfigurations": [] + }, + "conditions": [], + "name": "NewRewrite", + "ruleSequence": 100 + } + ] + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + "webApplicationFirewallConfiguration": { + "value": { + "disabledRuleGroups": [ + { + "ruleGroupName": "Known-CVEs" + }, + { + "ruleGroupName": "REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION" + }, + { + "ruleGroupName": "REQUEST-941-APPLICATION-ATTACK-XSS" + } + ], + "enabled": true, + "exclusions": [ + { + "matchVariable": "RequestHeaderNames", + "selector": "hola", + "selectorMatchOperator": "StartsWith" + } + ], + "fileUploadLimitInMb": 100, + "firewallMode": "Detection", + "maxRequestBodySizeInKb": 128, + "requestBodyCheck": true, + "ruleSetType": "OWASP", + "ruleSetVersion": "3.0" + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

+ +### Example 3: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module applicationGateway 'br/public:avm/res/network/application-gateway:' = { + name: 'applicationGatewayDeployment' + params: { + // Required parameters + name: '' + // Non-required parameters + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + enableHttp2: true + enableTelemetry: '' + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: '' + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + privateLinkConfiguration: { + id: '' + } + publicIPAddress: { + id: '' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: '' + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '' + } + frontendPort: { + id: '' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + userAssignedResourceIds: [ + '' + ] + } + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + '' + ] + service: 'public' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + privateLinkConfigurations: [ + { + id: '' + name: 'pvtlink01' + properties: { + ipConfigurations: [ + { + id: '' + name: 'privateLinkIpConfig1' + properties: { + primary: false + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '' + } + } + } + ] + } + } + ] + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '' + } + ] + targetListener: { + id: '' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '' + } + backendHttpSettings: { + id: '' + } + httpListener: { + id: '' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '' + } + priority: 300 + redirectConfiguration: { + id: '' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '' + } + priority: 350 + redirectConfiguration: { + id: '' + } + rewriteRuleSet: { + id: '' + } + ruleType: 'Basic' + } + } + ] + rewriteRuleSets: [ + { + id: '' + name: 'customRewrite' + properties: { + rewriteRules: [ + { + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + conditions: [] + name: 'NewRewrite' + ruleSequence: 100 + } + ] + } + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: 'az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: '' + } + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + webApplicationFirewallConfiguration: { + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Prevention' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + zones: [ + '1' + '2' + '3' + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "appServiceBackendPool", + "properties": { + "backendAddresses": [ + { + "fqdn": "aghapp.azurewebsites.net" + } + ] + } + }, + { + "name": "privateVmBackendPool", + "properties": { + "backendAddresses": [ + { + "ipAddress": "10.0.0.4" + } + ] + } + } + ] + }, + "backendHttpSettingsCollection": { + "value": [ + { + "name": "appServiceBackendHttpsSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": true, + "port": 443, + "protocol": "Https", + "requestTimeout": 30 + } + }, + { + "name": "privateVmHttpSetting", + "properties": { + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "port": 80, + "probe": { + "id": "" + }, + "protocol": "Http", + "requestTimeout": 30 + } + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "enableHttp2": { + "value": true + }, + "enableTelemetry": { + "value": "" + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.0.20", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "privateLinkConfiguration": { + "id": "" + }, + "publicIPAddress": { + "id": "" + } + } + } + ] + }, + "frontendPorts": { + "value": [ + { + "name": "port443", + "properties": { + "port": 443 + } + }, + { + "name": "port4433", + "properties": { + "port": 4433 + } + }, + { + "name": "port80", + "properties": { + "port": 80 + } + }, + { + "name": "port8080", + "properties": { + "port": 8080 + } + } + ] + }, + "gatewayIPConfigurations": { + "value": [ + { + "name": "apw-ip-configuration", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "httpListeners": { + "value": [ + { + "name": "public443", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "private4433", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "https", + "requireServerNameIndication": false, + "sslCertificate": { + "id": "" + } + } + }, + { + "name": "httpRedirect80", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + }, + { + "name": "httpRedirect8080", + "properties": { + "frontendIPConfiguration": { + "id": "" + }, + "frontendPort": { + "id": "" + }, + "hostNames": [], + "protocol": "Http", + "requireServerNameIndication": false + } + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "" + ] + } + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneResourceIds": [ + "" + ], + "service": "public", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, + "privateLinkConfigurations": { + "value": [ + { + "id": "", + "name": "pvtlink01", + "properties": { + "ipConfigurations": [ + { + "id": "", + "name": "privateLinkIpConfig1", + "properties": { + "primary": false, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "" + } + } + } + ] + } + } + ] + }, + "probes": { + "value": [ + { + "name": "privateVmHttpSettingProbe", + "properties": { + "host": "10.0.0.4", + "interval": 60, + "match": { + "statusCodes": [ + "200", + "401" + ] + }, + "minServers": 3, + "path": "/", + "pickHostNameFromBackendHttpSettings": false, + "protocol": "Http", + "timeout": 15, + "unhealthyThreshold": 5 + } + } + ] + }, + "redirectConfigurations": { + "value": [ + { + "name": "httpRedirect80", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + }, + { + "name": "httpRedirect8080", + "properties": { + "includePath": true, + "includeQueryString": true, + "redirectType": "Permanent", + "requestRoutingRules": [ + { + "id": "" + } + ], + "targetListener": { + "id": "" + } + } + } + ] + }, + "requestRoutingRules": { + "value": [ + { + "name": "public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 200, + "ruleType": "Basic" + } + }, + { + "name": "private4433-privateVmHttpSetting-privateVmHttpSetting", + "properties": { + "backendAddressPool": { + "id": "" + }, + "backendHttpSettings": { + "id": "" + }, + "httpListener": { + "id": "" + }, + "priority": 250, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect80-public443", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 300, + "redirectConfiguration": { + "id": "" + }, + "ruleType": "Basic" + } + }, + { + "name": "httpRedirect8080-private4433", + "properties": { + "httpListener": { + "id": "" + }, + "priority": 350, + "redirectConfiguration": { + "id": "" + }, + "rewriteRuleSet": { + "id": "" + }, + "ruleType": "Basic" + } + } + ] + }, + "rewriteRuleSets": { + "value": [ + { + "id": "", + "name": "customRewrite", + "properties": { + "rewriteRules": [ + { + "actionSet": { + "requestHeaderConfigurations": [ + { + "headerName": "Content-Type", + "headerValue": "JSON" + }, + { + "headerName": "someheader" + } + ], + "responseHeaderConfigurations": [] + }, + "conditions": [], + "name": "NewRewrite", + "ruleSequence": 100 + } + ] + } + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + "webApplicationFirewallConfiguration": { + "value": { + "enabled": true, + "fileUploadLimitInMb": 100, + "firewallMode": "Prevention", + "maxRequestBodySizeInKb": 128, + "requestBodyCheck": true, + "ruleSetType": "OWASP", + "ruleSetVersion": "3.0" + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the Application Gateway. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`authenticationCertificates`](#parameter-authenticationcertificates) | array | Authentication certificates of the application gateway resource. | +| [`autoscaleMaxCapacity`](#parameter-autoscalemaxcapacity) | int | Upper bound on number of Application Gateway capacity. | +| [`autoscaleMinCapacity`](#parameter-autoscalemincapacity) | int | Lower bound on number of Application Gateway capacity. | +| [`backendAddressPools`](#parameter-backendaddresspools) | array | Backend address pool of the application gateway resource. | +| [`backendHttpSettingsCollection`](#parameter-backendhttpsettingscollection) | array | Backend http settings of the application gateway resource. | +| [`backendSettingsCollection`](#parameter-backendsettingscollection) | array | Backend settings of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits). | +| [`capacity`](#parameter-capacity) | int | The number of Application instances to be configured. | +| [`customErrorConfigurations`](#parameter-customerrorconfigurations) | array | Custom error configurations of the application gateway resource. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableFips`](#parameter-enablefips) | bool | Whether FIPS is enabled on the application gateway resource. | +| [`enableHttp2`](#parameter-enablehttp2) | bool | Whether HTTP2 is enabled on the application gateway resource. | +| [`enableRequestBuffering`](#parameter-enablerequestbuffering) | bool | Enable request buffering. | +| [`enableResponseBuffering`](#parameter-enableresponsebuffering) | bool | Enable response buffering. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`firewallPolicyId`](#parameter-firewallpolicyid) | string | The resource ID of an associated firewall policy. Should be configured for security reasons. | +| [`frontendIPConfigurations`](#parameter-frontendipconfigurations) | array | Frontend IP addresses of the application gateway resource. | +| [`frontendPorts`](#parameter-frontendports) | array | Frontend ports of the application gateway resource. | +| [`gatewayIPConfigurations`](#parameter-gatewayipconfigurations) | array | Subnets of the application gateway resource. | +| [`httpListeners`](#parameter-httplisteners) | array | Http listeners of the application gateway resource. | +| [`listeners`](#parameter-listeners) | array | Listeners of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits). | +| [`loadDistributionPolicies`](#parameter-loaddistributionpolicies) | array | Load distribution policies of the application gateway resource. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`privateLinkConfigurations`](#parameter-privatelinkconfigurations) | array | PrivateLink configurations on application gateway. | +| [`probes`](#parameter-probes) | array | Probes of the application gateway resource. | +| [`redirectConfigurations`](#parameter-redirectconfigurations) | array | Redirect configurations of the application gateway resource. | +| [`requestRoutingRules`](#parameter-requestroutingrules) | array | Request routing rules of the application gateway resource. | +| [`rewriteRuleSets`](#parameter-rewriterulesets) | array | Rewrite rules for the application gateway resource. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`routingRules`](#parameter-routingrules) | array | Routing rules of the application gateway resource. | +| [`sku`](#parameter-sku) | string | The name of the SKU for the Application Gateway. | +| [`sslCertificates`](#parameter-sslcertificates) | array | SSL certificates of the application gateway resource. | +| [`sslPolicyCipherSuites`](#parameter-sslpolicyciphersuites) | array | Ssl cipher suites to be enabled in the specified order to application gateway. | +| [`sslPolicyMinProtocolVersion`](#parameter-sslpolicyminprotocolversion) | string | Ssl protocol enums. | +| [`sslPolicyName`](#parameter-sslpolicyname) | string | Ssl predefined policy name enums. | +| [`sslPolicyType`](#parameter-sslpolicytype) | string | Type of Ssl Policy. | +| [`sslProfiles`](#parameter-sslprofiles) | array | SSL profiles of the application gateway resource. | +| [`tags`](#parameter-tags) | object | Resource tags. | +| [`trustedClientCertificates`](#parameter-trustedclientcertificates) | array | Trusted client certificates of the application gateway resource. | +| [`trustedRootCertificates`](#parameter-trustedrootcertificates) | array | Trusted Root certificates of the application gateway resource. | +| [`urlPathMaps`](#parameter-urlpathmaps) | array | URL path map of the application gateway resource. | +| [`webApplicationFirewallConfiguration`](#parameter-webapplicationfirewallconfiguration) | object | Application gateway web application firewall configuration. Should be configured for security reasons. | +| [`zones`](#parameter-zones) | array | A list of availability zones denoting where the resource needs to come from. | + +### Parameter: `name` + +Name of the Application Gateway. + +- Required: Yes +- Type: string + +### Parameter: `authenticationCertificates` + +Authentication certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `autoscaleMaxCapacity` + +Upper bound on number of Application Gateway capacity. + +- Required: No +- Type: int +- Default: `-1` + +### Parameter: `autoscaleMinCapacity` + +Lower bound on number of Application Gateway capacity. + +- Required: No +- Type: int +- Default: `-1` + +### Parameter: `backendAddressPools` + +Backend address pool of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `backendHttpSettingsCollection` + +Backend http settings of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `backendSettingsCollection` + +Backend settings of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits). + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `capacity` + +The number of Application instances to be configured. + +- Required: No +- Type: int +- Default: `2` + +### Parameter: `customErrorConfigurations` + +Custom error configurations of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `enableFips` + +Whether FIPS is enabled on the application gateway resource. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableHttp2` + +Whether HTTP2 is enabled on the application gateway resource. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableRequestBuffering` + +Enable request buffering. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableResponseBuffering` + +Enable response buffering. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `firewallPolicyId` + +The resource ID of an associated firewall policy. Should be configured for security reasons. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `frontendIPConfigurations` + +Frontend IP addresses of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `frontendPorts` + +Frontend ports of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `gatewayIPConfigurations` + +Subnets of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `httpListeners` + +Http listeners of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `listeners` + +Listeners of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits). + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `loadDistributionPolicies` + +Load distribution policies of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. + +- Required: Yes +- Type: array + +### Parameter: `privateEndpoints` + +Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". | +| [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. | +| [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. | +| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | +| [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | +| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | +| [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | +| [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | +| [`privateDnsZoneGroupName`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. | +| [`privateDnsZoneResourceIds`](#parameter-privateendpointsprivatednszoneresourceids) | array | The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | +| [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | +| [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | + +### Parameter: `privateEndpoints.service` + +The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file". + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.subnetResourceId` + +Resource ID of the subnet where the endpoint needs to be created. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds` + +Application security groups in which the private endpoint IP configuration is included. + +- Required: No +- Type: array + +### Parameter: `privateEndpoints.customDnsConfigs` + +Custom DNS configurations. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint IP address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private IP addresses of the private endpoint. | + +### Parameter: `privateEndpoints.customDnsConfigs.fqdn` + +Fqdn that resolves to private endpoint IP address. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` + +A list of private IP addresses of the private endpoint. + +- Required: Yes +- Type: array + +### Parameter: `privateEndpoints.customNetworkInterfaceName` + +The custom name of the network interface attached to the private endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.ipConfigurations` + +A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsipconfigurationsname) | string | The name of the resource that is unique within a resource group. | +| [`properties`](#parameter-privateendpointsipconfigurationsproperties) | object | Properties of private endpoint IP configurations. | + +### Parameter: `privateEndpoints.ipConfigurations.name` + +The name of the resource that is unique within a resource group. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties` + +Properties of private endpoint IP configurations. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | +| [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private IP address obtained from the private endpoint's subnet. | + +### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` + +The ID of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties.memberName` + +The member name of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` + +A private IP address obtained from the private endpoint's subnet. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.location` + +The location to deploy the private endpoint to. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.lock` + +Specify the type of lock. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-privateendpointslockkind) | string | Specify the type of lock. | +| [`name`](#parameter-privateendpointslockname) | string | Specify the name of lock. | + +### Parameter: `privateEndpoints.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `privateEndpoints.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.manualConnectionRequestMessage` + +A message passed to the owner of the remote resource with the manual connection request. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.name` + +The name of the private endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroupName` + +The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneResourceIds` + +The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones. + +- Required: No +- Type: array + +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-privateendpointsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-privateendpointsroleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-privateendpointsroleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `privateEndpoints.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `privateEndpoints.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `privateEndpoints.tags` + +Tags to be applied on all resources/resource groups in this deployment. + +- Required: No +- Type: object + +### Parameter: `privateLinkConfigurations` + +PrivateLink configurations on application gateway. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `probes` + +Probes of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `redirectConfigurations` + +Redirect configurations of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `requestRoutingRules` + +Request routing rules of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `rewriteRuleSets` + +Rewrite rules for the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `routingRules` + +Routing rules of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `sku` + +The name of the SKU for the Application Gateway. + +- Required: No +- Type: string +- Default: `'WAF_v2'` +- Allowed: + ```Bicep + [ + 'Standard_Large' + 'Standard_Medium' + 'Standard_Small' + 'Standard_v2' + 'WAF_Large' + 'WAF_Medium' + 'WAF_v2' + ] + ``` + +### Parameter: `sslCertificates` + +SSL certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `sslPolicyCipherSuites` + +Ssl cipher suites to be enabled in the specified order to application gateway. + +- Required: No +- Type: array +- Default: + ```Bicep + [ + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + ] + ``` +- Allowed: + ```Bicep + [ + 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256' + 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256' + 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA' + 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA' + 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA' + 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256' + 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA' + 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384' + 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA' + 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA' + 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_RSA_WITH_3DES_EDE_CBC_SHA' + 'TLS_RSA_WITH_AES_128_CBC_SHA' + 'TLS_RSA_WITH_AES_128_CBC_SHA256' + 'TLS_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_RSA_WITH_AES_256_CBC_SHA' + 'TLS_RSA_WITH_AES_256_CBC_SHA256' + 'TLS_RSA_WITH_AES_256_GCM_SHA384' + ] + ``` + +### Parameter: `sslPolicyMinProtocolVersion` + +Ssl protocol enums. + +- Required: No +- Type: string +- Default: `'TLSv1_2'` +- Allowed: + ```Bicep + [ + 'TLSv1_0' + 'TLSv1_1' + 'TLSv1_2' + 'TLSv1_3' + ] + ``` + +### Parameter: `sslPolicyName` + +Ssl predefined policy name enums. + +- Required: No +- Type: string +- Default: `''` +- Allowed: + ```Bicep + [ + '' + 'AppGwSslPolicy20150501' + 'AppGwSslPolicy20170401' + 'AppGwSslPolicy20170401S' + 'AppGwSslPolicy20220101' + 'AppGwSslPolicy20220101S' + ] + ``` + +### Parameter: `sslPolicyType` + +Type of Ssl Policy. + +- Required: No +- Type: string +- Default: `'Custom'` +- Allowed: + ```Bicep + [ + 'Custom' + 'CustomV2' + 'Predefined' + ] + ``` + +### Parameter: `sslProfiles` + +SSL profiles of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `tags` + +Resource tags. + +- Required: No +- Type: object + +### Parameter: `trustedClientCertificates` + +Trusted client certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `trustedRootCertificates` + +Trusted Root certificates of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `urlPathMaps` + +URL path map of the application gateway resource. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `webApplicationFirewallConfiguration` + +Application gateway web application firewall configuration. Should be configured for security reasons. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `zones` + +A list of availability zones denoting where the resource needs to come from. + +- Required: No +- Type: array +- Default: `[]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application gateway. | +| `resourceGroupName` | string | The resource group the application gateway was deployed into. | +| `resourceId` | string | The resource ID of the application gateway. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/network/private-endpoint:0.4.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/network/application-gateway/main.bicep b/avm/res/network/application-gateway/main.bicep new file mode 100644 index 0000000000..6aa9a00ea9 --- /dev/null +++ b/avm/res/network/application-gateway/main.bicep @@ -0,0 +1,616 @@ +metadata name = 'Network Application Gateways' +metadata description = 'This module deploys a Network Application Gateway.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the Application Gateway.') +@maxLength(80) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The managed identity definition for this resource.') +param managedIdentities managedIdentitiesType + +@description('Optional. Authentication certificates of the application gateway resource.') +param authenticationCertificates array = [] + +@description('Optional. Upper bound on number of Application Gateway capacity.') +param autoscaleMaxCapacity int = -1 + +@description('Optional. Lower bound on number of Application Gateway capacity.') +param autoscaleMinCapacity int = -1 + +@description('Optional. Backend address pool of the application gateway resource.') +param backendAddressPools array = [] + +@description('Optional. Backend http settings of the application gateway resource.') +param backendHttpSettingsCollection array = [] + +@description('Optional. Custom error configurations of the application gateway resource.') +param customErrorConfigurations array = [] + +@description('Optional. Whether FIPS is enabled on the application gateway resource.') +param enableFips bool = false + +@description('Optional. Whether HTTP2 is enabled on the application gateway resource.') +param enableHttp2 bool = false + +@description('Optional. The resource ID of an associated firewall policy. Should be configured for security reasons.') +param firewallPolicyId string = '' + +@description('Optional. Frontend IP addresses of the application gateway resource.') +param frontendIPConfigurations array = [] + +@description('Optional. Frontend ports of the application gateway resource.') +param frontendPorts array = [] + +@description('Optional. Subnets of the application gateway resource.') +param gatewayIPConfigurations array = [] + +@description('Optional. Enable request buffering.') +param enableRequestBuffering bool = false + +@description('Optional. Enable response buffering.') +param enableResponseBuffering bool = false + +@description('Optional. Http listeners of the application gateway resource.') +param httpListeners array = [] + +@description('Optional. Load distribution policies of the application gateway resource.') +param loadDistributionPolicies array = [] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints privateEndpointType + +@description('Optional. PrivateLink configurations on application gateway.') +param privateLinkConfigurations array = [] + +@description('Optional. Probes of the application gateway resource.') +param probes array = [] + +@description('Optional. Redirect configurations of the application gateway resource.') +param redirectConfigurations array = [] + +@description('Optional. Request routing rules of the application gateway resource.') +param requestRoutingRules array = [] + +@description('Optional. Rewrite rules for the application gateway resource.') +param rewriteRuleSets array = [] + +@description('Optional. The name of the SKU for the Application Gateway.') +@allowed([ + 'Standard_Small' + 'Standard_Medium' + 'Standard_Large' + 'WAF_Medium' + 'WAF_Large' + 'Standard_v2' + 'WAF_v2' +]) +param sku string = 'WAF_v2' + +@description('Optional. The number of Application instances to be configured.') +@minValue(0) +@maxValue(10) +param capacity int = 2 + +@description('Optional. SSL certificates of the application gateway resource.') +param sslCertificates array = [] + +@description('Optional. Ssl cipher suites to be enabled in the specified order to application gateway.') +@allowed([ + 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256' + 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA' + 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256' + 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA' + 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA' + 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA' + 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256' + 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA' + 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384' + 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA' + 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA' + 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_RSA_WITH_3DES_EDE_CBC_SHA' + 'TLS_RSA_WITH_AES_128_CBC_SHA' + 'TLS_RSA_WITH_AES_128_CBC_SHA256' + 'TLS_RSA_WITH_AES_128_GCM_SHA256' + 'TLS_RSA_WITH_AES_256_CBC_SHA' + 'TLS_RSA_WITH_AES_256_CBC_SHA256' + 'TLS_RSA_WITH_AES_256_GCM_SHA384' +]) +param sslPolicyCipherSuites array = [ + 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384' + 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' +] + +@description('Optional. Ssl protocol enums.') +@allowed([ + 'TLSv1_0' + 'TLSv1_1' + 'TLSv1_2' + 'TLSv1_3' +]) +param sslPolicyMinProtocolVersion string = 'TLSv1_2' + +@description('Optional. Ssl predefined policy name enums.') +@allowed([ + 'AppGwSslPolicy20150501' + 'AppGwSslPolicy20170401' + 'AppGwSslPolicy20170401S' + 'AppGwSslPolicy20220101' + 'AppGwSslPolicy20220101S' + '' +]) +param sslPolicyName string = '' + +@description('Optional. Type of Ssl Policy.') +@allowed([ + 'Custom' + 'CustomV2' + 'Predefined' +]) +param sslPolicyType string = 'Custom' + +@description('Optional. SSL profiles of the application gateway resource.') +param sslProfiles array = [] + +@description('Optional. Trusted client certificates of the application gateway resource.') +param trustedClientCertificates array = [] + +@description('Optional. Trusted Root certificates of the application gateway resource.') +param trustedRootCertificates array = [] + +@description('Optional. URL path map of the application gateway resource.') +param urlPathMaps array = [] + +@description('Optional. Application gateway web application firewall configuration. Should be configured for security reasons.') +param webApplicationFirewallConfiguration object = {} + +@description('Optional. A list of availability zones denoting where the resource needs to come from.') +param zones array = [] + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +var formattedUserAssignedIdentities = reduce( + map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), + {}, + (cur, next) => union(cur, next) +) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) + ? { + type: !empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Resource tags.') +param tags object? + +@description('Optional. Backend settings of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits).') +param backendSettingsCollection array = [] + +@description('Optional. Listeners of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits).') +param listeners array = [] + +@description('Optional. Routing rules of the application gateway resource.') +param routingRules array = [] + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.network-appgw.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +resource applicationGateway 'Microsoft.Network/applicationGateways@2023-04-01' = { + name: name + location: location + tags: tags + identity: identity + properties: union( + { + authenticationCertificates: authenticationCertificates + autoscaleConfiguration: autoscaleMaxCapacity > 0 && autoscaleMinCapacity >= 0 + ? { + maxCapacity: autoscaleMaxCapacity + minCapacity: autoscaleMinCapacity + } + : null + backendAddressPools: backendAddressPools + backendHttpSettingsCollection: backendHttpSettingsCollection + backendSettingsCollection: backendSettingsCollection + customErrorConfigurations: customErrorConfigurations + enableHttp2: enableHttp2 + firewallPolicy: !empty(firewallPolicyId) + ? { + id: firewallPolicyId + } + : null + forceFirewallPolicyAssociation: !empty(firewallPolicyId) + frontendIPConfigurations: frontendIPConfigurations + frontendPorts: frontendPorts + gatewayIPConfigurations: gatewayIPConfigurations + globalConfiguration: endsWith(sku, 'v2') + ? { + enableRequestBuffering: enableRequestBuffering + enableResponseBuffering: enableResponseBuffering + } + : null + httpListeners: httpListeners + loadDistributionPolicies: loadDistributionPolicies + listeners: listeners + privateLinkConfigurations: privateLinkConfigurations + probes: probes + redirectConfigurations: redirectConfigurations + requestRoutingRules: requestRoutingRules + routingRules: routingRules + rewriteRuleSets: rewriteRuleSets + sku: { + name: sku + tier: endsWith(sku, 'v2') ? sku : substring(sku, 0, indexOf(sku, '_')) + capacity: autoscaleMaxCapacity > 0 && autoscaleMinCapacity >= 0 ? null : capacity + } + sslCertificates: sslCertificates + sslPolicy: sslPolicyType != 'Predefined' + ? { + cipherSuites: sslPolicyCipherSuites + minProtocolVersion: sslPolicyMinProtocolVersion + policyName: empty(sslPolicyName) ? null : sslPolicyName + policyType: sslPolicyType + } + : { + policyName: empty(sslPolicyName) ? null : sslPolicyName + policyType: sslPolicyType + } + sslProfiles: sslProfiles + trustedClientCertificates: trustedClientCertificates + trustedRootCertificates: trustedRootCertificates + urlPathMaps: urlPathMaps + }, + (enableFips + ? { + enableFips: enableFips + } + : {}), + (!empty(webApplicationFirewallConfiguration) + ? { webApplicationFirewallConfiguration: webApplicationFirewallConfiguration } + : {}) + ) + zones: zones +} + +resource applicationGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = + if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: applicationGateway + } + +resource applicationGateway_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: applicationGateway + } +] + +module applicationGateway_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.4.1' = [ + for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-applicationGateway-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') + params: { + name: privateEndpoint.?name ?? 'pep-${last(split(applicationGateway.id, '/'))}-${privateEndpoint.service}-${index}' + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(applicationGateway.id, '/'))}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: applicationGateway.id + groupIds: [ + privateEndpoint.service + ] + } + } + ] + : null + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(applicationGateway.id, '/'))}-${privateEndpoint.service}-${index}' + properties: { + privateLinkServiceId: applicationGateway.id + groupIds: [ + privateEndpoint.service + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] + : null + subnetResourceId: privateEndpoint.subnetResourceId + enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry + location: privateEndpoint.?location ?? reference( + split(privateEndpoint.subnetResourceId, '/subnets/')[0], + '2020-06-01', + 'Full' + ).location + lock: privateEndpoint.?lock ?? lock + privateDnsZoneGroupName: privateEndpoint.?privateDnsZoneGroupName + privateDnsZoneResourceIds: privateEndpoint.?privateDnsZoneResourceIds + roleAssignments: privateEndpoint.?roleAssignments + tags: privateEndpoint.?tags ?? tags + customDnsConfigs: privateEndpoint.?customDnsConfigs + ipConfigurations: privateEndpoint.?ipConfigurations + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName + } + } +] + +resource applicationGateway_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(applicationGateway.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: applicationGateway + } +] + +@description('The name of the application gateway.') +output name string = applicationGateway.name + +@description('The resource ID of the application gateway.') +output resourceId string = applicationGateway.id + +@description('The resource group the application gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = applicationGateway.location + +// =============== // +// Definitions // +// =============== // + +type managedIdentitiesType = { + @description('Optional. The resource ID(s) to assign to the resource.') + userAssignedResourceIds: string[] +}? + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @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? + + @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? +}[]? + +type privateEndpointType = { + @description('Optional. The name of the private endpoint.') + name: string? + + @description('Optional. The location to deploy the private endpoint to.') + location: string? + + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + + @description('Required. The subresource to deploy the private endpoint for. For example "blob", "table", "queue" or "file".') + service: string + + @description('Required. Resource ID of the subnet where the endpoint needs to be created.') + subnetResourceId: string + + @description('Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided.') + privateDnsZoneGroupName: string? + + @description('Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneResourceIds: string[]? + + @description('Optional. If Manual Private Link Connection is required.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + + @description('Optional. Custom DNS configurations.') + customDnsConfigs: { + @description('Required. Fqdn that resolves to private endpoint IP address.') + fqdn: string? + + @description('Required. A list of private IP addresses of the private endpoint.') + ipAddresses: string[] + }[]? + + @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') + ipConfigurations: { + @description('Required. The name of the resource that is unique within a resource group.') + name: string + + @description('Required. Properties of private endpoint IP configurations.') + properties: { + @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.') + groupId: string + + @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') + memberName: string + + @description('Required. A private IP address obtained from the private endpoint\'s subnet.') + privateIPAddress: string + } + }[]? + + @description('Optional. Application security groups in which the private endpoint IP configuration is included.') + applicationSecurityGroupResourceIds: string[]? + + @description('Optional. The custom name of the network interface attached to the private endpoint.') + customNetworkInterfaceName: string? + + @description('Optional. Specify the type of lock.') + lock: lockType + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType + + @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') + tags: object? + + @description('Optional. Enable/Disable usage telemetry for module.') + enableTelemetry: bool? + + @description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.') + resourceGroupName: string? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/network/application-gateway/main.json b/avm/res/network/application-gateway/main.json new file mode 100644 index 0000000000..d775da8b70 --- /dev/null +++ b/avm/res/network/application-gateway/main.json @@ -0,0 +1,1633 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "12167944734183128583" + }, + "name": "Network Application Gateways", + "description": "This module deploys a Network Application Gateway.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 80, + "metadata": { + "description": "Required. Name of the Application Gateway." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "authenticationCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Authentication certificates of the application gateway resource." + } + }, + "autoscaleMaxCapacity": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Upper bound on number of Application Gateway capacity." + } + }, + "autoscaleMinCapacity": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. Lower bound on number of Application Gateway capacity." + } + }, + "backendAddressPools": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backend address pool of the application gateway resource." + } + }, + "backendHttpSettingsCollection": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backend http settings of the application gateway resource." + } + }, + "customErrorConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom error configurations of the application gateway resource." + } + }, + "enableFips": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether FIPS is enabled on the application gateway resource." + } + }, + "enableHttp2": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether HTTP2 is enabled on the application gateway resource." + } + }, + "firewallPolicyId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of an associated firewall policy. Should be configured for security reasons." + } + }, + "frontendIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Frontend IP addresses of the application gateway resource." + } + }, + "frontendPorts": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Frontend ports of the application gateway resource." + } + }, + "gatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Subnets of the application gateway resource." + } + }, + "enableRequestBuffering": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable request buffering." + } + }, + "enableResponseBuffering": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable response buffering." + } + }, + "httpListeners": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Http listeners of the application gateway resource." + } + }, + "loadDistributionPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Load distribution policies of the application gateway resource." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "privateLinkConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. PrivateLink configurations on application gateway." + } + }, + "probes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Probes of the application gateway resource." + } + }, + "redirectConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Redirect configurations of the application gateway resource." + } + }, + "requestRoutingRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Request routing rules of the application gateway resource." + } + }, + "rewriteRuleSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Rewrite rules for the application gateway resource." + } + }, + "sku": { + "type": "string", + "defaultValue": "WAF_v2", + "allowedValues": [ + "Standard_Small", + "Standard_Medium", + "Standard_Large", + "WAF_Medium", + "WAF_Large", + "Standard_v2", + "WAF_v2" + ], + "metadata": { + "description": "Optional. The name of the SKU for the Application Gateway." + } + }, + "capacity": { + "type": "int", + "defaultValue": 2, + "minValue": 0, + "maxValue": 10, + "metadata": { + "description": "Optional. The number of Application instances to be configured." + } + }, + "sslCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SSL certificates of the application gateway resource." + } + }, + "sslPolicyCipherSuites": { + "type": "array", + "defaultValue": [ + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + ], + "allowedValues": [ + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384" + ], + "metadata": { + "description": "Optional. Ssl cipher suites to be enabled in the specified order to application gateway." + } + }, + "sslPolicyMinProtocolVersion": { + "type": "string", + "defaultValue": "TLSv1_2", + "allowedValues": [ + "TLSv1_0", + "TLSv1_1", + "TLSv1_2", + "TLSv1_3" + ], + "metadata": { + "description": "Optional. Ssl protocol enums." + } + }, + "sslPolicyName": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "AppGwSslPolicy20150501", + "AppGwSslPolicy20170401", + "AppGwSslPolicy20170401S", + "AppGwSslPolicy20220101", + "AppGwSslPolicy20220101S", + "" + ], + "metadata": { + "description": "Optional. Ssl predefined policy name enums." + } + }, + "sslPolicyType": { + "type": "string", + "defaultValue": "Custom", + "allowedValues": [ + "Custom", + "CustomV2", + "Predefined" + ], + "metadata": { + "description": "Optional. Type of Ssl Policy." + } + }, + "sslProfiles": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. SSL profiles of the application gateway resource." + } + }, + "trustedClientCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Trusted client certificates of the application gateway resource." + } + }, + "trustedRootCertificates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Trusted Root certificates of the application gateway resource." + } + }, + "urlPathMaps": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. URL path map of the application gateway resource." + } + }, + "webApplicationFirewallConfiguration": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Application gateway web application firewall configuration. Should be configured for security reasons." + } + }, + "zones": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of availability zones denoting where the resource needs to come from." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "backendSettingsCollection": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Backend settings of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits)." + } + }, + "listeners": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Listeners of the application gateway resource. For default limits, see [Application Gateway limits](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits#application-gateway-limits)." + } + }, + "routingRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Routing rules of the application gateway resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-appgw.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "applicationGateway": { + "type": "Microsoft.Network/applicationGateways", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": "[union(createObject('authenticationCertificates', parameters('authenticationCertificates'), 'autoscaleConfiguration', if(and(greater(parameters('autoscaleMaxCapacity'), 0), greaterOrEquals(parameters('autoscaleMinCapacity'), 0)), createObject('maxCapacity', parameters('autoscaleMaxCapacity'), 'minCapacity', parameters('autoscaleMinCapacity')), null()), 'backendAddressPools', parameters('backendAddressPools'), 'backendHttpSettingsCollection', parameters('backendHttpSettingsCollection'), 'backendSettingsCollection', parameters('backendSettingsCollection'), 'customErrorConfigurations', parameters('customErrorConfigurations'), 'enableHttp2', parameters('enableHttp2'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'forceFirewallPolicyAssociation', not(empty(parameters('firewallPolicyId'))), 'frontendIPConfigurations', parameters('frontendIPConfigurations'), 'frontendPorts', parameters('frontendPorts'), 'gatewayIPConfigurations', parameters('gatewayIPConfigurations'), 'globalConfiguration', if(endsWith(parameters('sku'), 'v2'), createObject('enableRequestBuffering', parameters('enableRequestBuffering'), 'enableResponseBuffering', parameters('enableResponseBuffering')), null()), 'httpListeners', parameters('httpListeners'), 'loadDistributionPolicies', parameters('loadDistributionPolicies'), 'listeners', parameters('listeners'), 'privateLinkConfigurations', parameters('privateLinkConfigurations'), 'probes', parameters('probes'), 'redirectConfigurations', parameters('redirectConfigurations'), 'requestRoutingRules', parameters('requestRoutingRules'), 'routingRules', parameters('routingRules'), 'rewriteRuleSets', parameters('rewriteRuleSets'), 'sku', createObject('name', parameters('sku'), 'tier', if(endsWith(parameters('sku'), 'v2'), parameters('sku'), substring(parameters('sku'), 0, indexOf(parameters('sku'), '_'))), 'capacity', if(and(greater(parameters('autoscaleMaxCapacity'), 0), greaterOrEquals(parameters('autoscaleMinCapacity'), 0)), null(), parameters('capacity'))), 'sslCertificates', parameters('sslCertificates'), 'sslPolicy', if(not(equals(parameters('sslPolicyType'), 'Predefined')), createObject('cipherSuites', parameters('sslPolicyCipherSuites'), 'minProtocolVersion', parameters('sslPolicyMinProtocolVersion'), 'policyName', if(empty(parameters('sslPolicyName')), null(), parameters('sslPolicyName')), 'policyType', parameters('sslPolicyType')), createObject('policyName', if(empty(parameters('sslPolicyName')), null(), parameters('sslPolicyName')), 'policyType', parameters('sslPolicyType'))), 'sslProfiles', parameters('sslProfiles'), 'trustedClientCertificates', parameters('trustedClientCertificates'), 'trustedRootCertificates', parameters('trustedRootCertificates'), 'urlPathMaps', parameters('urlPathMaps')), if(parameters('enableFips'), createObject('enableFips', parameters('enableFips')), createObject()), if(not(empty(parameters('webApplicationFirewallConfiguration'))), createObject('webApplicationFirewallConfiguration', parameters('webApplicationFirewallConfiguration')), createObject()))]", + "zones": "[parameters('zones')]" + }, + "applicationGateway_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/applicationGateways/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "applicationGateway" + ] + }, + "applicationGateway_diagnosticSettings": { + "copy": { + "name": "applicationGateway_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/applicationGateways/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "applicationGateway" + ] + }, + "applicationGateway_roleAssignments": { + "copy": { + "name": "applicationGateway_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/applicationGateways/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/applicationGateways', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "applicationGateway" + ] + }, + "applicationGateway_privateEndpoints": { + "copy": { + "name": "applicationGateway_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-applicationGateway-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Network/applicationGateways', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Network/applicationGateways', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Network/applicationGateways', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Network/applicationGateways', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Network/applicationGateways', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "applicationGateway" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the application gateway." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application gateway." + }, + "value": "[resourceId('Microsoft.Network/applicationGateways', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the application gateway was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('applicationGateway', '2023-04-01', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep b/avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..4f1950dc66 --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Public IP to create.') +param publicIPName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + } + } + { + name: 'privateLinkSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network default subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id diff --git a/avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep b/avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..0998d2c453 --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,146 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' +param resourceGroupName string = 'dep-${namePrefix}-network.applicationgateway-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nagmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var resourceName = '${namePrefix}${serviceShort}001' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + // You parameters go here + name: resourceName + zones: [ + '1' + '2' + '3' + ] + location: resourceLocation + gatewayIPConfigurations: [ + { + name: 'publicIPConfig1' + properties: { + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + ] + frontendIPConfigurations: [ + { + name: 'frontendIPConfig1' + properties: { + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + } + } + ] + frontendPorts: [ + { + name: 'frontendPort1' + properties: { + port: 80 + } + } + ] + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + ] + backendHttpSettingsCollection: [ + { + name: 'backendHttpSettings1' + properties: { + port: 80 + protocol: 'Http' + cookieBasedAffinity: 'Disabled' + } + } + ] + httpListeners: [ + { + name: 'httpListener1' + properties: { + hostName: 'www.contoso.com' + protocol: 'Http' + frontendIPConfiguration: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/frontendIPConfigurations/frontendIPConfig1' + } + frontendPort: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/frontendPorts/frontendPort1' + } + } + } + ] + requestRoutingRules: [ + { + name: 'requestRoutingRule1' + properties: { + ruleType: 'Basic' + priority: 100 + httpListener: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/httpListeners/httpListener1' + } + backendAddressPool: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/backendAddressPools/backendAddressPool1' + } + backendHttpSettings: { + id: '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${resourceName}/backendHttpSettingsCollection/backendHttpSettings1' + } + } + } + ] + webApplicationFirewallConfiguration: { + enabled: false + } + } + } +] diff --git a/avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep b/avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..2a9c2b0fae --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/max/dependencies.bicep @@ -0,0 +1,154 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Public IP to create.') +param publicIPName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Deployment Script to create for the Certificate generation.') +param certDeploymentScriptName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + } + } + { + name: 'privateLinkSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.appgateway.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '00482a5a-887f-4fb3-b363-3b7fe8e74483' + ) // Key Vault Administrator + principalType: 'ServicePrincipal' + } +} + +resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: certDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-KeyVaultName "${keyVault.name}" -CertName "applicationGatewaySslCertificate"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-CertificateInKeyVault.ps1') + } +} + +@description('The resource ID of the created Virtual Network default subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Virtual Network private link subnet.') +output privateLinkSubnetResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The URL of the created certificate.') +output certificateSecretUrl string = certDeploymentScript.properties.outputs.secretUrl + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/network/application-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/application-gateway/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..7e67afba91 --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/max/main.test.bicep @@ -0,0 +1,524 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.applicationgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nagmax' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableTelemetry bool = true + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var appGWName = '${namePrefix}${serviceShort}001' +var appGWExpectedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${appGWName}' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + enableTelemetry: enableTelemetry + name: appGWName + zones: [ + '1' + '2' + '3' + ] + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '${appGWExpectedResourceID}/probes/privateVmHttpSettingProbe' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + enableHttp2: true + privateLinkConfigurations: [ + { + name: 'pvtlink01' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + properties: { + ipConfigurations: [ + { + name: 'privateLinkIpConfig1' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01/ipConfigurations/privateLinkIpConfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + primary: false + subnet: { + id: nestedDependencies.outputs.privateLinkSubnetResourceId + } + } + } + ] + } + } + ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'public' + subnetResourceId: nestedDependencies.outputs.privateLinkSubnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + privateLinkConfiguration: { + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port443' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port4433' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port80' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port8080' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect80-public443' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect8080-private4433' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/appServiceBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/appServiceBackendHttpsSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/privateVmBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/privateVmHttpSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect80' + } + priority: 300 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect80' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect8080' + } + priority: 350 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect8080' + } + ruleType: 'Basic' + rewriteRuleSet: { + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + } + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '${namePrefix}-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: nestedDependencies.outputs.certificateSecretUrl + } + } + ] + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + rewriteRuleSets: [ + { + name: 'customRewrite' + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + properties: { + rewriteRules: [ + { + ruleSequence: 100 + conditions: [] + name: 'NewRewrite' + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + } + ] + } + } + ] + webApplicationFirewallConfiguration: { + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Detection' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + disabledRuleGroups: [ + { + ruleGroupName: 'Known-CVEs' + } + { + ruleGroupName: 'REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION' + } + { + ruleGroupName: 'REQUEST-941-APPLICATION-ATTACK-XSS' + } + ] + exclusions: [ + { + matchVariable: 'RequestHeaderNames' + selectorMatchOperator: 'StartsWith' + selector: 'hola' + } + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..2a9c2b0fae --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,154 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Public IP to create.') +param publicIPName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Required. The name of the Deployment Script to create for the Certificate generation.') +param certDeploymentScriptName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + } + } + { + name: 'privateLinkSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.appgateway.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: null + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${managedIdentity.name}-KeyVault-Admin-RoleAssignment') + scope: keyVault + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '00482a5a-887f-4fb3-b363-3b7fe8e74483' + ) // Key Vault Administrator + principalType: 'ServicePrincipal' + } +} + +resource certDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: certDeploymentScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-KeyVaultName "${keyVault.name}" -CertName "applicationGatewaySslCertificate"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-CertificateInKeyVault.ps1') + } +} + +@description('The resource ID of the created Virtual Network default subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created Virtual Network private link subnet.') +output privateLinkSubnetResourceId string = virtualNetwork.properties.subnets[1].id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The URL of the created certificate.') +output certificateSecretUrl string = certDeploymentScript.properties.outputs.secretUrl + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..c6f9dede4f --- /dev/null +++ b/avm/res/network/application-gateway/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,486 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.applicationgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nagwaf' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableTelemetry bool = true + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + certDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var appGWName = '${namePrefix}${serviceShort}001' +var appGWExpectedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${appGWName}' +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + enableTelemetry: enableTelemetry + name: appGWName + zones: [ + '1' + '2' + '3' + ] + backendAddressPools: [ + { + name: 'appServiceBackendPool' + properties: { + backendAddresses: [ + { + fqdn: 'aghapp.azurewebsites.net' + } + ] + } + } + { + name: 'privateVmBackendPool' + properties: { + backendAddresses: [ + { + ipAddress: '10.0.0.4' + } + ] + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'appServiceBackendHttpsSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: true + port: 443 + protocol: 'Https' + requestTimeout: 30 + } + } + { + name: 'privateVmHttpSetting' + properties: { + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + port: 80 + probe: { + id: '${appGWExpectedResourceID}/probes/privateVmHttpSettingProbe' + } + protocol: 'Http' + requestTimeout: 30 + } + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + enableHttp2: true + privateLinkConfigurations: [ + { + name: 'pvtlink01' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + properties: { + ipConfigurations: [ + { + name: 'privateLinkIpConfig1' + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01/ipConfigurations/privateLinkIpConfig1' + properties: { + privateIPAllocationMethod: 'Dynamic' + primary: false + subnet: { + id: nestedDependencies.outputs.privateLinkSubnetResourceId + } + } + } + ] + } + } + ] + privateEndpoints: [ + { + privateDnsZoneResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + service: 'public' + subnetResourceId: nestedDependencies.outputs.privateLinkSubnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + privateLinkConfiguration: { + id: '${appGWExpectedResourceID}/privateLinkConfigurations/pvtlink01' + } + } + } + ] + frontendPorts: [ + { + name: 'port443' + properties: { + port: 443 + } + } + { + name: 'port4433' + properties: { + port: 4433 + } + } + { + name: 'port80' + properties: { + port: 80 + } + } + { + name: 'port8080' + properties: { + port: 8080 + } + } + ] + gatewayIPConfigurations: [ + { + name: 'apw-ip-configuration' + properties: { + subnet: { + id: nestedDependencies.outputs.defaultSubnetResourceId + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port443' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'private4433' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port4433' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/${namePrefix}-az-apgw-x-001-ssl-certificate' + } + } + } + { + name: 'httpRedirect80' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port80' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + { + name: 'httpRedirect8080' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/private' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port8080' + } + hostNames: [] + protocol: 'Http' + requireServerNameIndication: false + } + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + probes: [ + { + name: 'privateVmHttpSettingProbe' + properties: { + host: '10.0.0.4' + interval: 60 + match: { + statusCodes: [ + '200' + '401' + ] + } + minServers: 3 + path: '/' + pickHostNameFromBackendHttpSettings: false + protocol: 'Http' + timeout: 15 + unhealthyThreshold: 5 + } + } + ] + redirectConfigurations: [ + { + name: 'httpRedirect80' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect80-public443' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + } + } + { + name: 'httpRedirect8080' + properties: { + includePath: true + includeQueryString: true + redirectType: 'Permanent' + requestRoutingRules: [ + { + id: '${appGWExpectedResourceID}/requestRoutingRules/httpRedirect8080-private4433' + } + ] + targetListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + } + } + ] + requestRoutingRules: [ + { + name: 'public443-appServiceBackendHttpsSetting-appServiceBackendHttpsSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/appServiceBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/appServiceBackendHttpsSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/public443' + } + priority: 200 + ruleType: 'Basic' + } + } + { + name: 'private4433-privateVmHttpSetting-privateVmHttpSetting' + properties: { + backendAddressPool: { + id: '${appGWExpectedResourceID}/backendAddressPools/privateVmBackendPool' + } + backendHttpSettings: { + id: '${appGWExpectedResourceID}/backendHttpSettingsCollection/privateVmHttpSetting' + } + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/private4433' + } + priority: 250 + ruleType: 'Basic' + } + } + { + name: 'httpRedirect80-public443' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect80' + } + priority: 300 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect80' + } + ruleType: 'Basic' + } + } + { + name: 'httpRedirect8080-private4433' + properties: { + httpListener: { + id: '${appGWExpectedResourceID}/httpListeners/httpRedirect8080' + } + priority: 350 + redirectConfiguration: { + id: '${appGWExpectedResourceID}/redirectConfigurations/httpRedirect8080' + } + ruleType: 'Basic' + rewriteRuleSet: { + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + } + } + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '${namePrefix}-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: nestedDependencies.outputs.certificateSecretUrl + } + } + ] + managedIdentities: { + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + rewriteRuleSets: [ + { + name: 'customRewrite' + id: '${appGWExpectedResourceID}/rewriteRuleSets/customRewrite' + properties: { + rewriteRules: [ + { + ruleSequence: 100 + conditions: [] + name: 'NewRewrite' + actionSet: { + requestHeaderConfigurations: [ + { + headerName: 'Content-Type' + headerValue: 'JSON' + } + { + headerName: 'someheader' + } + ] + responseHeaderConfigurations: [] + } + } + ] + } + } + ] + webApplicationFirewallConfiguration: { + enabled: true + fileUploadLimitInMb: 100 + firewallMode: 'Prevention' + maxRequestBodySizeInKb: 128 + requestBodyCheck: true + ruleSetType: 'OWASP' + ruleSetVersion: '3.0' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/application-gateway/version.json b/avm/res/network/application-gateway/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/network/application-gateway/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 4d1e57a4d242ad35088e3ab37526d276dbc4bb7d Mon Sep 17 00:00:00 2001 From: Nate Arnold Date: Fri, 19 Apr 2024 09:54:43 -0600 Subject: [PATCH 58/66] feat: `avm/res/sql/managed-instance` (#1618) Migration of SQL Managed Instance module from CARML to AVM ## Pipeline Reference [![avm.res.sql.managed-instance](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml/badge.svg?branch=test%2Fsqlmi)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: Alexander Sehr --- .github/CODEOWNERS | 2 +- .github/ISSUE_TEMPLATE/avm_module_issue.yml | 2 +- .../avm.res.sql.managed-instance.yml | 85 + avm/res/sql/managed-instance/README.md | 1485 ++++++++++++ .../managed-instance/administrator/README.md | 84 + .../managed-instance/administrator/main.bicep | 39 + .../managed-instance/administrator/main.json | 77 + .../sql/managed-instance/database/README.md | 349 +++ .../README.md | 111 + .../main.bicep | 52 + .../main.json | 98 + .../README.md | 84 + .../main.bicep | 40 + .../main.json | 74 + .../sql/managed-instance/database/main.bicep | 198 ++ .../sql/managed-instance/database/main.json | 596 +++++ .../encryption-protector/README.md | 92 + .../encryption-protector/main.bicep | 42 + .../encryption-protector/main.json | 81 + avm/res/sql/managed-instance/key/README.md | 92 + avm/res/sql/managed-instance/key/main.bicep | 47 + avm/res/sql/managed-instance/key/main.json | 84 + avm/res/sql/managed-instance/main.bicep | 460 ++++ avm/res/sql/managed-instance/main.json | 1986 +++++++++++++++++ .../security-alert-policy/README.md | 92 + .../security-alert-policy/main.bicep | 41 + .../security-alert-policy/main.json | 80 + .../tests/e2e/defaults/dependencies.bicep | 288 +++ .../tests/e2e/defaults/main.test.bicep | 64 + .../tests/e2e/max/dependencies.bicep | 326 +++ .../tests/e2e/max/main.test.bicep | 189 ++ .../tests/e2e/vulnAssm/dependencies.bicep | 386 ++++ .../tests/e2e/vulnAssm/main.test.bicep | 90 + .../tests/e2e/waf-aligned/dependencies.bicep | 326 +++ .../tests/e2e/waf-aligned/main.test.bicep | 172 ++ avm/res/sql/managed-instance/version.json | 7 + .../vulnerability-assessment/README.md | 121 + .../vulnerability-assessment/main.bicep | 64 + .../vulnerability-assessment/main.json | 167 ++ .../nested_storageRoleAssignment.bicep | 20 + 40 files changed, 8691 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/avm.res.sql.managed-instance.yml create mode 100644 avm/res/sql/managed-instance/README.md create mode 100644 avm/res/sql/managed-instance/administrator/README.md create mode 100644 avm/res/sql/managed-instance/administrator/main.bicep create mode 100644 avm/res/sql/managed-instance/administrator/main.json create mode 100644 avm/res/sql/managed-instance/database/README.md create mode 100644 avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md create mode 100644 avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep create mode 100644 avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json create mode 100644 avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md create mode 100644 avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep create mode 100644 avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json create mode 100644 avm/res/sql/managed-instance/database/main.bicep create mode 100644 avm/res/sql/managed-instance/database/main.json create mode 100644 avm/res/sql/managed-instance/encryption-protector/README.md create mode 100644 avm/res/sql/managed-instance/encryption-protector/main.bicep create mode 100644 avm/res/sql/managed-instance/encryption-protector/main.json create mode 100644 avm/res/sql/managed-instance/key/README.md create mode 100644 avm/res/sql/managed-instance/key/main.bicep create mode 100644 avm/res/sql/managed-instance/key/main.json create mode 100644 avm/res/sql/managed-instance/main.bicep create mode 100644 avm/res/sql/managed-instance/main.json create mode 100644 avm/res/sql/managed-instance/security-alert-policy/README.md create mode 100644 avm/res/sql/managed-instance/security-alert-policy/main.bicep create mode 100644 avm/res/sql/managed-instance/security-alert-policy/main.json create mode 100644 avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/sql/managed-instance/version.json create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/README.md create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/main.bicep create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/main.json create mode 100644 avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e04de640ac..0734b0fb91 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -131,7 +131,7 @@ /avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/signal-r/ @Azure/avm-res-signalrservice-signalr-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/web-pub-sub/ @Azure/avm-res-signalrservice-webpubsub-module-owners-bicep @Azure/avm-core-team-technical-bicep -#/avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/sql/server/ @Azure/avm-res-sql-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/storage/storage-account/ @Azure/avm-res-storage-storageaccount-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/synapse/private-link-hub/ @Azure/avm-res-synapse-privatelinkhub-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 40c763986f..8ad0c07acd 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -164,7 +164,7 @@ body: - "avm/res/service-fabric/cluster" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" - # - "avm/res/sql/managed-instance" + - "avm/res/sql/managed-instance" - "avm/res/sql/server" - "avm/res/storage/storage-account" - "avm/res/synapse/private-link-hub" diff --git a/.github/workflows/avm.res.sql.managed-instance.yml b/.github/workflows/avm.res.sql.managed-instance.yml new file mode 100644 index 0000000000..54bb6be439 --- /dev/null +++ b/.github/workflows/avm.res.sql.managed-instance.yml @@ -0,0 +1,85 @@ +name: "avm.res.sql.managed-instance" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.sql.managed-instance.yml" + - "avm/res/sql/managed-instance/**" + - "avm/utilities/pipelines/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/sql/managed-instance" + workflowPath: ".github/workflows/avm.res.sql.managed-instance.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/sql/managed-instance/README.md b/avm/res/sql/managed-instance/README.md new file mode 100644 index 0000000000..1d06c5a612 --- /dev/null +++ b/avm/res/sql/managed-instance/README.md @@ -0,0 +1,1485 @@ +# SQL Managed Instances `[Microsoft.Sql/managedInstances]` + +This module deploys a SQL Managed Instance. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/managedInstances` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances) | +| `Microsoft.Sql/managedInstances/administrators` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/administrators) | +| `Microsoft.Sql/managedInstances/databases` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases) | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/encryptionProtector` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/encryptionProtector) | +| `Microsoft.Sql/managedInstances/keys` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/keys) | +| `Microsoft.Sql/managedInstances/securityAlertPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/securityAlertPolicies) | +| `Microsoft.Sql/managedInstances/vulnerabilityAssessments` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/vulnerabilityAssessments) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/sql/managed-instance:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [With vulnerability assessment](#example-3-with-vulnerability-assessment) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmimin' + subnetResourceId: '' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmimin" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Using large parameter set_ + +This instance deploys the module with most of its features enabled. + + +

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmimax' + subnetResourceId: '' + // Non-required parameters + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + name: 'sqlmimax-db-001' + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '' + serverKeyType: 'AzureKeyVault' + uri: '' + } + ] + licenseType: 'LicenseIncluded' + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + '' + ] + } + primaryUserAssignedIdentityId: '' + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmimax" + }, + "subnetResourceId": { + "value": "" + }, + "collation": { + "value": "SQL_Latin1_General_CP1_CI_AS" + }, + "databases": { + "value": [ + { + "backupLongTermRetentionPolicies": { + "name": "default" + }, + "backupShortTermRetentionPolicies": { + "name": "default" + }, + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "name": "sqlmimax-db-001" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "dnsZonePartner": { + "value": "" + }, + "encryptionProtectorObj": { + "value": { + "serverKeyName": "", + "serverKeyType": "AzureKeyVault" + } + }, + "hardwareFamily": { + "value": "Gen5" + }, + "keys": { + "value": [ + { + "name": "", + "serverKeyType": "AzureKeyVault", + "uri": "" + } + ] + }, + "licenseType": { + "value": "LicenseIncluded" + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "systemAssigned": true, + "userAssignedResourceIds": [ + "" + ] + } + }, + "primaryUserAssignedIdentityId": { + "value": "" + }, + "proxyOverride": { + "value": "Proxy" + }, + "publicDataEndpointEnabled": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "servicePrincipal": { + "value": "SystemAssigned" + }, + "skuName": { + "value": "GP_Gen5" + }, + "skuTier": { + "value": "GeneralPurpose" + }, + "storageSizeInGB": { + "value": 32 + }, + "timezoneId": { + "value": "UTC" + }, + "vCores": { + "value": 4 + }, + "vulnerabilityAssessmentsObj": { + "value": { + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "storageAccountResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } + } +} +``` + +
+

+ +### Example 3: _With vulnerability assessment_ + +This instance deploys the module with a vulnerability assessment. + + +

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmivln' + subnetResourceId: '' + // Non-required parameters + location: '' + managedIdentities: { + systemAssigned: true + } + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + vulnerabilityAssessmentsObj: { + createStorageRoleAssignment: true + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + useStorageAccountAccessKey: false + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmivln" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "vulnerabilityAssessmentsObj": { + "value": { + "createStorageRoleAssignment": true, + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "storageAccountResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + }, + "useStorageAccountAccessKey": false + } + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module managedInstance 'br/public:avm/res/sql/managed-instance:' = { + name: 'managedInstanceDeployment' + params: { + // Required parameters + administratorLogin: 'adminUserName' + administratorLoginPassword: '' + name: 'sqlmiwaf' + subnetResourceId: '' + // Non-required parameters + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + name: 'sqlmiwaf-db-001' + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '' + serverKeyType: 'AzureKeyVault' + uri: '' + } + ] + licenseType: 'LicenseIncluded' + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + '' + ] + } + primaryUserAssignedIdentityId: '' + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "administratorLogin": { + "value": "adminUserName" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "sqlmiwaf" + }, + "subnetResourceId": { + "value": "" + }, + "collation": { + "value": "SQL_Latin1_General_CP1_CI_AS" + }, + "databases": { + "value": [ + { + "backupLongTermRetentionPolicies": { + "name": "default" + }, + "backupShortTermRetentionPolicies": { + "name": "default" + }, + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "name": "sqlmiwaf-db-001" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "dnsZonePartner": { + "value": "" + }, + "encryptionProtectorObj": { + "value": { + "serverKeyName": "", + "serverKeyType": "AzureKeyVault" + } + }, + "hardwareFamily": { + "value": "Gen5" + }, + "keys": { + "value": [ + { + "name": "", + "serverKeyType": "AzureKeyVault", + "uri": "" + } + ] + }, + "licenseType": { + "value": "LicenseIncluded" + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "managedIdentities": { + "value": { + "systemAssigned": true, + "userAssignedResourceIds": [ + "" + ] + } + }, + "primaryUserAssignedIdentityId": { + "value": "" + }, + "proxyOverride": { + "value": "Proxy" + }, + "publicDataEndpointEnabled": { + "value": false + }, + "securityAlertPoliciesObj": { + "value": { + "emailAccountAdmins": true, + "name": "default", + "state": "Enabled" + } + }, + "servicePrincipal": { + "value": "SystemAssigned" + }, + "skuName": { + "value": "GP_Gen5" + }, + "skuTier": { + "value": "GeneralPurpose" + }, + "storageSizeInGB": { + "value": 32 + }, + "timezoneId": { + "value": "UTC" + }, + "vCores": { + "value": 4 + }, + "vulnerabilityAssessmentsObj": { + "value": { + "emailSubscriptionAdmins": true, + "name": "default", + "recurringScansEmails": [ + "test1@contoso.com", + "test2@contoso.com" + ], + "recurringScansIsEnabled": true, + "storageAccountResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorLogin`](#parameter-administratorlogin) | string | The username used to establish jumpbox VMs. | +| [`administratorLoginPassword`](#parameter-administratorloginpassword) | securestring | The password given to the admin user. | +| [`name`](#parameter-name) | string | The name of the SQL managed instance. | +| [`subnetResourceId`](#parameter-subnetresourceid) | string | The fully qualified resource ID of the subnet on which the SQL managed instance will be placed. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`primaryUserAssignedIdentityId`](#parameter-primaryuserassignedidentityid) | string | The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorsObj`](#parameter-administratorsobj) | object | The administrator configuration. | +| [`collation`](#parameter-collation) | string | Collation of the managed instance. | +| [`databases`](#parameter-databases) | array | Databases to create in this server. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`dnsZonePartner`](#parameter-dnszonepartner) | string | The resource ID of another managed instance whose DNS zone this managed instance will share after creation. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`encryptionProtectorObj`](#parameter-encryptionprotectorobj) | object | The encryption protection configuration. | +| [`hardwareFamily`](#parameter-hardwarefamily) | string | If the service has different generations of hardware, for the same SKU, then that can be captured here. | +| [`instancePoolResourceId`](#parameter-instancepoolresourceid) | string | The resource ID of the instance pool this managed server belongs to. | +| [`keys`](#parameter-keys) | array | The keys to configure. | +| [`licenseType`](#parameter-licensetype) | string | The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses). | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | +| [`managedInstanceCreateMode`](#parameter-managedinstancecreatemode) | string | Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified. | +| [`minimalTlsVersion`](#parameter-minimaltlsversion) | string | Minimal TLS version allowed. | +| [`proxyOverride`](#parameter-proxyoverride) | string | Connection type used for connecting to the instance. | +| [`publicDataEndpointEnabled`](#parameter-publicdataendpointenabled) | bool | Whether or not the public data endpoint is enabled. | +| [`requestedBackupStorageRedundancy`](#parameter-requestedbackupstorageredundancy) | string | The storage account type used to store backups for this database. | +| [`restorePointInTime`](#parameter-restorepointintime) | string | Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`securityAlertPoliciesObj`](#parameter-securityalertpoliciesobj) | object | The security alert policy configuration. | +| [`servicePrincipal`](#parameter-serviceprincipal) | string | Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal. | +| [`skuName`](#parameter-skuname) | string | The name of the SKU, typically, a letter + Number code, e.g. P3. | +| [`skuTier`](#parameter-skutier) | string | The tier or edition of the particular SKU, e.g. Basic, Premium. | +| [`sourceManagedInstanceId`](#parameter-sourcemanagedinstanceid) | string | The resource identifier of the source managed instance associated with create operation of this instance. | +| [`storageSizeInGB`](#parameter-storagesizeingb) | int | Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`timezoneId`](#parameter-timezoneid) | string | ID of the timezone. Allowed values are timezones supported by Windows. | +| [`vCores`](#parameter-vcores) | int | The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80. | +| [`vulnerabilityAssessmentsObj`](#parameter-vulnerabilityassessmentsobj) | object | The vulnerability assessment configuration. | +| [`zoneRedundant`](#parameter-zoneredundant) | bool | Whether or not multi-az is enabled. | + +### Parameter: `administratorLogin` + +The username used to establish jumpbox VMs. + +- Required: Yes +- Type: string + +### Parameter: `administratorLoginPassword` + +The password given to the admin user. + +- Required: Yes +- Type: securestring + +### Parameter: `name` + +The name of the SQL managed instance. + +- Required: Yes +- Type: string + +### Parameter: `subnetResourceId` + +The fully qualified resource ID of the subnet on which the SQL managed instance will be placed. + +- Required: Yes +- Type: string + +### Parameter: `primaryUserAssignedIdentityId` + +The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `administratorsObj` + +The administrator configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `collation` + +Collation of the managed instance. + +- Required: No +- Type: string +- Default: `'SQL_Latin1_General_CP1_CI_AS'` + +### Parameter: `databases` + +Databases to create in this server. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `dnsZonePartner` + +The resource ID of another managed instance whose DNS zone this managed instance will share after creation. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `encryptionProtectorObj` + +The encryption protection configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `hardwareFamily` + +If the service has different generations of hardware, for the same SKU, then that can be captured here. + +- Required: No +- Type: string +- Default: `'Gen5'` + +### Parameter: `instancePoolResourceId` + +The resource ID of the instance pool this managed server belongs to. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `keys` + +The keys to configure. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `licenseType` + +The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses). + +- Required: No +- Type: string +- Default: `'LicenseIncluded'` +- Allowed: + ```Bicep + [ + 'BasePrice' + 'LicenseIncluded' + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. + +- Required: No +- Type: array + +### Parameter: `managedInstanceCreateMode` + +Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified. + +- Required: No +- Type: string +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'PointInTimeRestore' + ] + ``` + +### Parameter: `minimalTlsVersion` + +Minimal TLS version allowed. + +- Required: No +- Type: string +- Default: `'1.2'` +- Allowed: + ```Bicep + [ + '1.0' + '1.1' + '1.2' + 'None' + ] + ``` + +### Parameter: `proxyOverride` + +Connection type used for connecting to the instance. + +- Required: No +- Type: string +- Default: `'Proxy'` +- Allowed: + ```Bicep + [ + 'Default' + 'Proxy' + 'Redirect' + ] + ``` + +### Parameter: `publicDataEndpointEnabled` + +Whether or not the public data endpoint is enabled. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `requestedBackupStorageRedundancy` + +The storage account type used to store backups for this database. + +- Required: No +- Type: string +- Default: `'Geo'` +- Allowed: + ```Bicep + [ + 'Geo' + 'GeoZone' + 'Local' + 'Zone' + ] + ``` + +### Parameter: `restorePointInTime` + +Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | 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'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | 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". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +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'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +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". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `securityAlertPoliciesObj` + +The security alert policy configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `servicePrincipal` + +Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal. + +- Required: No +- Type: string +- Default: `'None'` +- Allowed: + ```Bicep + [ + 'None' + 'SystemAssigned' + ] + ``` + +### Parameter: `skuName` + +The name of the SKU, typically, a letter + Number code, e.g. P3. + +- Required: No +- Type: string +- Default: `'GP_Gen5'` + +### Parameter: `skuTier` + +The tier or edition of the particular SKU, e.g. Basic, Premium. + +- Required: No +- Type: string +- Default: `'GeneralPurpose'` + +### Parameter: `sourceManagedInstanceId` + +The resource identifier of the source managed instance associated with create operation of this instance. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `storageSizeInGB` + +Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only. + +- Required: No +- Type: int +- Default: `32` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `timezoneId` + +ID of the timezone. Allowed values are timezones supported by Windows. + +- Required: No +- Type: string +- Default: `'UTC'` + +### Parameter: `vCores` + +The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80. + +- Required: No +- Type: int +- Default: `4` + +### Parameter: `vulnerabilityAssessmentsObj` + +The vulnerability assessment configuration. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `zoneRedundant` + +Whether or not multi-az is enabled. + +- Required: No +- Type: bool +- Default: `False` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed managed instance. | +| `resourceGroupName` | string | The resource group of the deployed managed instance. | +| `resourceId` | string | The resource ID of the deployed managed instance. | +| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/administrator/README.md b/avm/res/sql/managed-instance/administrator/README.md new file mode 100644 index 0000000000..87b2ba4cd3 --- /dev/null +++ b/avm/res/sql/managed-instance/administrator/README.md @@ -0,0 +1,84 @@ +# SQL Managed Instances Administrator `[Microsoft.Sql/managedInstances/administrators]` + +This module deploys a SQL Managed Instance Administrator. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/administrators` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/administrators) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`login`](#parameter-login) | string | Login name of the managed instance administrator. | +| [`sid`](#parameter-sid) | string | SID (object ID) of the managed instance administrator. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`tenantId`](#parameter-tenantid) | string | Tenant ID of the managed instance administrator. | + +### Parameter: `login` + +Login name of the managed instance administrator. + +- Required: Yes +- Type: string + +### Parameter: `sid` + +SID (object ID) of the managed instance administrator. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `tenantId` + +Tenant ID of the managed instance administrator. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance administrator. | +| `resourceGroupName` | string | The resource group of the deployed managed instance administrator. | +| `resourceId` | string | The resource ID of the deployed managed instance administrator. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/administrator/main.bicep b/avm/res/sql/managed-instance/administrator/main.bicep new file mode 100644 index 0000000000..2b6fcc3275 --- /dev/null +++ b/avm/res/sql/managed-instance/administrator/main.bicep @@ -0,0 +1,39 @@ +metadata name = 'SQL Managed Instances Administrator' +metadata description = 'This module deploys a SQL Managed Instance Administrator.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Required. Login name of the managed instance administrator.') +param login string + +@description('Required. SID (object ID) of the managed instance administrator.') +param sid string + +@description('Optional. Tenant ID of the managed instance administrator.') +param tenantId string = '' + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource administrator 'Microsoft.Sql/managedInstances/administrators@2023-08-01-preview' = { + name: 'ActiveDirectory' + parent: managedInstance + properties: { + administratorType: 'ActiveDirectory' + login: login + sid: sid + tenantId: tenantId + } +} + +@description('The name of the deployed managed instance administrator.') +output name string = administrator.name + +@description('The resource ID of the deployed managed instance administrator.') +output resourceId string = administrator.id + +@description('The resource group of the deployed managed instance administrator.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/administrator/main.json b/avm/res/sql/managed-instance/administrator/main.json new file mode 100644 index 0000000000..bda17b0156 --- /dev/null +++ b/avm/res/sql/managed-instance/administrator/main.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "14195697460339552085" + }, + "name": "SQL Managed Instances Administrator", + "description": "This module deploys a SQL Managed Instance Administrator.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "login": { + "type": "string", + "metadata": { + "description": "Required. Login name of the managed instance administrator." + } + }, + "sid": { + "type": "string", + "metadata": { + "description": "Required. SID (object ID) of the managed instance administrator." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Tenant ID of the managed instance administrator." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/administrators", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'ActiveDirectory')]", + "properties": { + "administratorType": "ActiveDirectory", + "login": "[parameters('login')]", + "sid": "[parameters('sid')]", + "tenantId": "[parameters('tenantId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance administrator." + }, + "value": "ActiveDirectory" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance administrator." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/administrators', parameters('managedInstanceName'), 'ActiveDirectory')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance administrator." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/database/README.md b/avm/res/sql/managed-instance/database/README.md new file mode 100644 index 0000000000..fcf9c4ed6c --- /dev/null +++ b/avm/res/sql/managed-instance/database/README.md @@ -0,0 +1,349 @@ +# SQL Managed Instance Databases `[Microsoft.Sql/managedInstances/databases]` + +This module deploys a SQL Managed Instance Database. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Sql/managedInstances/databases` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases) | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the SQL managed instance database. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`longTermRetentionBackupResourceId`](#parameter-longtermretentionbackupresourceid) | string | The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup. | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | +| [`recoverableDatabaseId`](#parameter-recoverabledatabaseid) | string | The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery. | +| [`restorePointInTime`](#parameter-restorepointintime) | string | Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore. | +| [`sourceDatabaseId`](#parameter-sourcedatabaseid) | string | The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore. | +| [`storageContainerSasToken`](#parameter-storagecontainersastoken) | securestring | Specifies the storage container sas token. Required if createMode is RestoreExternalBackup. | +| [`storageContainerUri`](#parameter-storagecontaineruri) | string | Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`backupLongTermRetentionPoliciesObj`](#parameter-backuplongtermretentionpoliciesobj) | object | The configuration for the backup long term retention policy definition. | +| [`backupShortTermRetentionPoliciesObj`](#parameter-backupshorttermretentionpoliciesobj) | object | The configuration for the backup short term retention policy definition. | +| [`catalogCollation`](#parameter-catalogcollation) | string | Collation of the managed instance. | +| [`collation`](#parameter-collation) | string | Collation of the managed instance database. | +| [`createMode`](#parameter-createmode) | string | Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required). | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`restorableDroppedDatabaseId`](#parameter-restorabledroppeddatabaseid) | string | The restorable dropped database resource ID to restore when creating this database. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `name` + +The name of the SQL managed instance database. + +- Required: Yes +- Type: string + +### Parameter: `longTermRetentionBackupResourceId` + +The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `recoverableDatabaseId` + +The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `restorePointInTime` + +Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `sourceDatabaseId` + +The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `storageContainerSasToken` + +Specifies the storage container sas token. Required if createMode is RestoreExternalBackup. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `storageContainerUri` + +Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `backupLongTermRetentionPoliciesObj` + +The configuration for the backup long term retention policy definition. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `backupShortTermRetentionPoliciesObj` + +The configuration for the backup short term retention policy definition. + +- Required: No +- Type: object +- Default: `{}` + +### Parameter: `catalogCollation` + +Collation of the managed instance. + +- Required: No +- Type: string +- Default: `'SQL_Latin1_General_CP1_CI_AS'` + +### Parameter: `collation` + +Collation of the managed instance database. + +- Required: No +- Type: string +- Default: `'SQL_Latin1_General_CP1_CI_AS'` + +### Parameter: `createMode` + +Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required). + +- Required: No +- Type: string +- Default: `'Default'` +- Allowed: + ```Bicep + [ + 'Default' + 'PointInTimeRestore' + 'Recovery' + 'RestoreExternalBackup' + 'RestoreLongTermRetentionBackup' + ] + ``` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to '' to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `restorableDroppedDatabaseId` + +The restorable dropped database resource ID to restore when creating this database. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed database. | +| `resourceGroupName` | string | The resource group the database was deployed into. | +| `resourceId` | string | The resource ID of the deployed database. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md new file mode 100644 index 0000000000..9bd34cadb3 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/README.md @@ -0,0 +1,111 @@ +# SQL Managed Instance Database Backup Long-Term Retention Policies `[Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies]` + +This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/databases/backupLongTermRetentionPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Long Term Retention backup policy. For example "default". | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseName`](#parameter-databasename) | string | The name of the parent managed instance database. Required if the template is used in a standalone deployment. | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`monthlyRetention`](#parameter-monthlyretention) | string | The monthly retention policy for an LTR backup in an ISO 8601 format. | +| [`weeklyRetention`](#parameter-weeklyretention) | string | The weekly retention policy for an LTR backup in an ISO 8601 format. | +| [`weekOfYear`](#parameter-weekofyear) | int | The week of year to take the yearly backup in an ISO 8601 format. | +| [`yearlyRetention`](#parameter-yearlyretention) | string | The yearly retention policy for an LTR backup in an ISO 8601 format. | + +### Parameter: `name` + +The name of the Long Term Retention backup policy. For example "default". + +- Required: Yes +- Type: string + +### Parameter: `databaseName` + +The name of the parent managed instance database. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `monthlyRetention` + +The monthly retention policy for an LTR backup in an ISO 8601 format. + +- Required: No +- Type: string +- Default: `'P1Y'` + +### Parameter: `weeklyRetention` + +The weekly retention policy for an LTR backup in an ISO 8601 format. + +- Required: No +- Type: string +- Default: `'P1M'` + +### Parameter: `weekOfYear` + +The week of year to take the yearly backup in an ISO 8601 format. + +- Required: No +- Type: int +- Default: `5` + +### Parameter: `yearlyRetention` + +The yearly retention policy for an LTR backup in an ISO 8601 format. + +- Required: No +- Type: string +- Default: `'P5Y'` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database backup long-term retention policy. | +| `resourceGroupName` | string | The resource group of the deployed database backup long-term retention policy. | +| `resourceId` | string | The resource ID of the deployed database backup long-term retention policy. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep new file mode 100644 index 0000000000..52ce6d2b23 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.bicep @@ -0,0 +1,52 @@ +metadata name = 'SQL Managed Instance Database Backup Long-Term Retention Policies' +metadata description = 'This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the Long Term Retention backup policy. For example "default".') +param name string + +@description('Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment.') +param databaseName string + +@description('Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The week of year to take the yearly backup in an ISO 8601 format.') +param weekOfYear int = 5 + +@description('Optional. The weekly retention policy for an LTR backup in an ISO 8601 format.') +param weeklyRetention string = 'P1M' + +@description('Optional. The monthly retention policy for an LTR backup in an ISO 8601 format.') +param monthlyRetention string = 'P1Y' + +@description('Optional. The yearly retention policy for an LTR backup in an ISO 8601 format.') +param yearlyRetention string = 'P5Y' + +resource managedInstance 'Microsoft.Sql/managedInstances@2022-05-01-preview' existing = { + name: managedInstanceName + + resource managedInstaceDatabase 'databases@2022-05-01-preview' existing = { + name: databaseName + } +} + +resource backupLongTermRetentionPolicy 'Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies@2022-05-01-preview' = { + name: name + parent: managedInstance::managedInstaceDatabase + properties: { + monthlyRetention: monthlyRetention + weeklyRetention: weeklyRetention + weekOfYear: weekOfYear + yearlyRetention: yearlyRetention + } +} + +@description('The name of the deployed database backup long-term retention policy.') +output name string = backupLongTermRetentionPolicy.name + +@description('The resource ID of the deployed database backup long-term retention policy.') +output resourceId string = backupLongTermRetentionPolicy.id + +@description('The resource group of the deployed database backup long-term retention policy.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json new file mode 100644 index 0000000000..3edd496f59 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-long-term-retention-policy/main.json @@ -0,0 +1,98 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "2315344113299493188" + }, + "name": "SQL Managed Instance Database Backup Long-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Long Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The week of year to take the yearly backup in an ISO 8601 format." + } + }, + "weeklyRetention": { + "type": "string", + "defaultValue": "P1M", + "metadata": { + "description": "Optional. The weekly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "monthlyRetention": { + "type": "string", + "defaultValue": "P1Y", + "metadata": { + "description": "Optional. The monthly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "yearlyRetention": { + "type": "string", + "defaultValue": "P5Y", + "metadata": { + "description": "Optional. The yearly retention policy for an LTR backup in an ISO 8601 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup long-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup long-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup long-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md new file mode 100644 index 0000000000..623130d636 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/README.md @@ -0,0 +1,84 @@ +# SQL Managed Instance Database Backup Short-Term Retention Policies `[Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies]` + +This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the Short Term Retention backup policy. For example "default". | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`databaseName`](#parameter-databasename) | string | The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment. | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`retentionDays`](#parameter-retentiondays) | int | The backup retention period in days. This is how many days Point-in-Time Restore will be supported. | + +### Parameter: `name` + +The name of the Short Term Retention backup policy. For example "default". + +- Required: Yes +- Type: string + +### Parameter: `databaseName` + +The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `retentionDays` + +The backup retention period in days. This is how many days Point-in-Time Restore will be supported. + +- Required: No +- Type: int +- Default: `35` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed database backup short-term retention policy. | +| `resourceGroupName` | string | The resource group of the deployed database backup short-term retention policy. | +| `resourceId` | string | The resource ID of the deployed database backup short-term retention policy. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep new file mode 100644 index 0000000000..207d0d314e --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.bicep @@ -0,0 +1,40 @@ +metadata name = 'SQL Managed Instance Database Backup Short-Term Retention Policies' +metadata description = 'This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the Short Term Retention backup policy. For example "default".') +param name string + +@description('Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment.') +param databaseName string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported.') +param retentionDays int = 35 + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName + + resource managedInstaceDatabase 'databases@2023-08-01-preview' existing = { + name: databaseName + } +} + +resource backupShortTermRetentionPolicy 'Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies@2023-08-01-preview' = { + name: name + parent: managedInstance::managedInstaceDatabase + properties: { + retentionDays: retentionDays + } +} + +@description('The name of the deployed database backup short-term retention policy.') +output name string = backupShortTermRetentionPolicy.name + +@description('The resource ID of the deployed database backup short-term retention policy.') +output resourceId string = backupShortTermRetentionPolicy.id + +@description('The resource group of the deployed database backup short-term retention policy.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json new file mode 100644 index 0000000000..4cee96ffa9 --- /dev/null +++ b/avm/res/sql/managed-instance/database/backup-short-term-retention-policy/main.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4816864047763535590" + }, + "name": "SQL Managed Instance Database Backup Short-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Short Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 35, + "metadata": { + "description": "Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup short-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup short-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup short-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/database/main.bicep b/avm/res/sql/managed-instance/database/main.bicep new file mode 100644 index 0000000000..a5180004e9 --- /dev/null +++ b/avm/res/sql/managed-instance/database/main.bicep @@ -0,0 +1,198 @@ +metadata name = 'SQL Managed Instance Databases' +metadata description = 'This module deploys a SQL Managed Instance Database.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the SQL managed instance database.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Collation of the managed instance database.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Collation of the managed instance.') +param catalogCollation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required).') +@allowed([ + 'Default' + 'RestoreExternalBackup' + 'PointInTimeRestore' + 'Recovery' + 'RestoreLongTermRetentionBackup' +]) +param createMode string = 'Default' + +@description('Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore.') +param sourceDatabaseId string = '' + +@description('Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore.') +param restorePointInTime string = '' + +@description('Optional. The restorable dropped database resource ID to restore when creating this database.') +param restorableDroppedDatabaseId string = '' + +@description('Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup.') +param storageContainerUri string = '' + +@description('Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup.') +@secure() +param storageContainerSasToken string = '' + +@description('Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery.') +param recoverableDatabaseId string = '' + +@description('Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup.') +param longTermRetentionBackupResourceId string = '' + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. The configuration for the backup short term retention policy definition.') +param backupShortTermRetentionPoliciesObj object = {} + +@description('Optional. The configuration for the backup long term retention policy definition.') +param backupLongTermRetentionPoliciesObj object = {} + +@description('Optional. Tags of the resource.') +param tags object? + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource database 'Microsoft.Sql/managedInstances/databases@2023-08-01-preview' = { + name: name + parent: managedInstance + location: location + tags: tags + properties: { + collation: empty(collation) ? null : collation + restorePointInTime: empty(restorePointInTime) ? null : restorePointInTime + catalogCollation: empty(catalogCollation) ? null : catalogCollation + createMode: empty(createMode) ? null : createMode + storageContainerUri: empty(storageContainerUri) ? null : storageContainerUri + sourceDatabaseId: empty(sourceDatabaseId) ? null : sourceDatabaseId + restorableDroppedDatabaseId: empty(restorableDroppedDatabaseId) ? null : restorableDroppedDatabaseId + storageContainerSasToken: empty(storageContainerSasToken) ? null : storageContainerSasToken + recoverableDatabaseId: empty(recoverableDatabaseId) ? null : recoverableDatabaseId + longTermRetentionBackupResourceId: empty(longTermRetentionBackupResourceId) ? null : longTermRetentionBackupResourceId + } +} + +resource database_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' + } + scope: database +} + +resource database_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' } ]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: database + } +] + +module database_backupShortTermRetentionPolicy 'backup-short-term-retention-policy/main.bicep' = if (!empty(backupShortTermRetentionPoliciesObj)) { + name: '${deployment().name}-BackupShortTRetPol' + params: { + managedInstanceName: managedInstanceName + databaseName: last(split(database.name, '/'))! + name: backupShortTermRetentionPoliciesObj.name + retentionDays: contains(backupShortTermRetentionPoliciesObj, 'retentionDays') ? backupShortTermRetentionPoliciesObj.retentionDays : 35 + } +} + +module database_backupLongTermRetentionPolicy 'backup-long-term-retention-policy/main.bicep' = if (!empty(backupLongTermRetentionPoliciesObj)) { + name: '${deployment().name}-BackupLongTRetPol' + params: { + managedInstanceName: managedInstanceName + databaseName: last(split(database.name, '/'))! + name: backupLongTermRetentionPoliciesObj.name + weekOfYear: contains(backupLongTermRetentionPoliciesObj, 'weekOfYear') ? backupLongTermRetentionPoliciesObj.weekOfYear : 5 + weeklyRetention: contains(backupLongTermRetentionPoliciesObj, 'weeklyRetention') ? backupLongTermRetentionPoliciesObj.weeklyRetention : 'P1M' + monthlyRetention: contains(backupLongTermRetentionPoliciesObj, 'monthlyRetention') ? backupLongTermRetentionPoliciesObj.monthlyRetention : 'P1Y' + yearlyRetention: contains(backupLongTermRetentionPoliciesObj, 'yearlyRetention') ? backupLongTermRetentionPoliciesObj.yearlyRetention : 'P5Y' + } +} + +@description('The name of the deployed database.') +output name string = database.name + +@description('The resource ID of the deployed database.') +output resourceId string = database.id + +@description('The resource group the database was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = database.location + +// =============== // +// Definitions // +// =============== // + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to \'\' to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to \'AllLogs\' to collect all logs.') + categoryGroup: string? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/sql/managed-instance/database/main.json b/avm/res/sql/managed-instance/database/main.json new file mode 100644 index 0000000000..8ae32849aa --- /dev/null +++ b/avm/res/sql/managed-instance/database/main.json @@ -0,0 +1,596 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15831945430809011826" + }, + "name": "SQL Managed Instance Databases", + "description": "This module deploys a SQL Managed Instance Database.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance database." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance database." + } + }, + "catalogCollation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "RestoreExternalBackup", + "PointInTimeRestore", + "Recovery", + "RestoreLongTermRetentionBackup" + ], + "metadata": { + "description": "Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required)." + } + }, + "sourceDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore." + } + }, + "restorePointInTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore." + } + }, + "restorableDroppedDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The restorable dropped database resource ID to restore when creating this database." + } + }, + "storageContainerUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup." + } + }, + "storageContainerSasToken": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup." + } + }, + "recoverableDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "backupShortTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup short term retention policy definition." + } + }, + "backupLongTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup long term retention policy definition." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "managedInstance": { + "existing": true, + "type": "Microsoft.Sql/managedInstances", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('managedInstanceName')]" + }, + "database": { + "type": "Microsoft.Sql/managedInstances/databases", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "collation": "[if(empty(parameters('collation')), null(), parameters('collation'))]", + "restorePointInTime": "[if(empty(parameters('restorePointInTime')), null(), parameters('restorePointInTime'))]", + "catalogCollation": "[if(empty(parameters('catalogCollation')), null(), parameters('catalogCollation'))]", + "createMode": "[if(empty(parameters('createMode')), null(), parameters('createMode'))]", + "storageContainerUri": "[if(empty(parameters('storageContainerUri')), null(), parameters('storageContainerUri'))]", + "sourceDatabaseId": "[if(empty(parameters('sourceDatabaseId')), null(), parameters('sourceDatabaseId'))]", + "restorableDroppedDatabaseId": "[if(empty(parameters('restorableDroppedDatabaseId')), null(), parameters('restorableDroppedDatabaseId'))]", + "storageContainerSasToken": "[if(empty(parameters('storageContainerSasToken')), null(), parameters('storageContainerSasToken'))]", + "recoverableDatabaseId": "[if(empty(parameters('recoverableDatabaseId')), null(), parameters('recoverableDatabaseId'))]", + "longTermRetentionBackupResourceId": "[if(empty(parameters('longTermRetentionBackupResourceId')), null(), parameters('longTermRetentionBackupResourceId'))]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "database_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "database" + ] + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "database" + ] + }, + "database_backupShortTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupShortTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupShortTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupShortTermRetentionPoliciesObj').name]" + }, + "retentionDays": "[if(contains(parameters('backupShortTermRetentionPoliciesObj'), 'retentionDays'), createObject('value', parameters('backupShortTermRetentionPoliciesObj').retentionDays), createObject('value', 35))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4816864047763535590" + }, + "name": "SQL Managed Instance Database Backup Short-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Short Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 35, + "metadata": { + "description": "Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup short-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup short-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup short-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + }, + "database_backupLongTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupLongTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupLongTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupLongTermRetentionPoliciesObj').name]" + }, + "weekOfYear": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weekOfYear'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weekOfYear), createObject('value', 5))]", + "weeklyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weeklyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weeklyRetention), createObject('value', 'P1M'))]", + "monthlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'monthlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').monthlyRetention), createObject('value', 'P1Y'))]", + "yearlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'yearlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').yearlyRetention), createObject('value', 'P5Y'))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "2315344113299493188" + }, + "name": "SQL Managed Instance Database Backup Long-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Long Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The week of year to take the yearly backup in an ISO 8601 format." + } + }, + "weeklyRetention": { + "type": "string", + "defaultValue": "P1M", + "metadata": { + "description": "Optional. The weekly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "monthlyRetention": { + "type": "string", + "defaultValue": "P1Y", + "metadata": { + "description": "Optional. The monthly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "yearlyRetention": { + "type": "string", + "defaultValue": "P5Y", + "metadata": { + "description": "Optional. The yearly retention policy for an LTR backup in an ISO 8601 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup long-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup long-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup long-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the database was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('database', '2023-08-01-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/encryption-protector/README.md b/avm/res/sql/managed-instance/encryption-protector/README.md new file mode 100644 index 0000000000..18c0a5d1f8 --- /dev/null +++ b/avm/res/sql/managed-instance/encryption-protector/README.md @@ -0,0 +1,92 @@ +# SQL Managed Instance Encryption Protector `[Microsoft.Sql/managedInstances/encryptionProtector]` + +This module deploys a SQL Managed Instance Encryption Protector. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/encryptionProtector` | [2022-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2022-05-01-preview/managedInstances/encryptionProtector) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serverKeyName`](#parameter-serverkeyname) | string | The name of the SQL managed instance key. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`autoRotationEnabled`](#parameter-autorotationenabled) | bool | Key auto rotation opt-in flag. | +| [`serverKeyType`](#parameter-serverkeytype) | string | The encryption protector type like "ServiceManaged", "AzureKeyVault". | + +### Parameter: `serverKeyName` + +The name of the SQL managed instance key. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `autoRotationEnabled` + +Key auto rotation opt-in flag. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `serverKeyType` + +The encryption protector type like "ServiceManaged", "AzureKeyVault". + +- Required: No +- Type: string +- Default: `'ServiceManaged'` +- Allowed: + ```Bicep + [ + 'AzureKeyVault' + 'ServiceManaged' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance encryption protector. | +| `resourceGroupName` | string | The resource group of the deployed managed instance encryption protector. | +| `resourceId` | string | The resource ID of the deployed managed instance encryption protector. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/encryption-protector/main.bicep b/avm/res/sql/managed-instance/encryption-protector/main.bicep new file mode 100644 index 0000000000..d3b89a6f22 --- /dev/null +++ b/avm/res/sql/managed-instance/encryption-protector/main.bicep @@ -0,0 +1,42 @@ +metadata name = 'SQL Managed Instance Encryption Protector' +metadata description = 'This module deploys a SQL Managed Instance Encryption Protector.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Required. The name of the SQL managed instance key.') +param serverKeyName string + +@description('Optional. The encryption protector type like "ServiceManaged", "AzureKeyVault".') +@allowed([ + 'AzureKeyVault' + 'ServiceManaged' +]) +param serverKeyType string = 'ServiceManaged' + +@description('Optional. Key auto rotation opt-in flag.') +param autoRotationEnabled bool = false + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource encryptionProtector 'Microsoft.Sql/managedInstances/encryptionProtector@2022-05-01-preview' = { + name: 'current' + parent: managedInstance + properties: { + autoRotationEnabled: autoRotationEnabled + serverKeyName: serverKeyName + serverKeyType: serverKeyType + } +} + +@description('The name of the deployed managed instance encryption protector.') +output name string = encryptionProtector.name + +@description('The resource ID of the deployed managed instance encryption protector.') +output resourceId string = encryptionProtector.id + +@description('The resource group of the deployed managed instance encryption protector.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/encryption-protector/main.json b/avm/res/sql/managed-instance/encryption-protector/main.json new file mode 100644 index 0000000000..54edff7908 --- /dev/null +++ b/avm/res/sql/managed-instance/encryption-protector/main.json @@ -0,0 +1,81 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "13463643567330956322" + }, + "name": "SQL Managed Instance Encryption Protector", + "description": "This module deploys a SQL Managed Instance Encryption Protector.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance key." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "autoRotationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Key auto rotation opt-in flag." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/encryptionProtector", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'current')]", + "properties": { + "autoRotationEnabled": "[parameters('autoRotationEnabled')]", + "serverKeyName": "[parameters('serverKeyName')]", + "serverKeyType": "[parameters('serverKeyType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance encryption protector." + }, + "value": "current" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance encryption protector." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/encryptionProtector', parameters('managedInstanceName'), 'current')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance encryption protector." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/key/README.md b/avm/res/sql/managed-instance/key/README.md new file mode 100644 index 0000000000..3cc7f5cf6e --- /dev/null +++ b/avm/res/sql/managed-instance/key/README.md @@ -0,0 +1,92 @@ +# SQL Managed Instance Keys `[Microsoft.Sql/managedInstances/keys]` + +This module deploys a SQL Managed Instance Key. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/keys` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/keys) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the key. Must follow the [__] pattern. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`serverKeyType`](#parameter-serverkeytype) | string | The encryption protector type like "ServiceManaged", "AzureKeyVault". | +| [`uri`](#parameter-uri) | string | The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required. | + +### Parameter: `name` + +The name of the key. Must follow the [__] pattern. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `serverKeyType` + +The encryption protector type like "ServiceManaged", "AzureKeyVault". + +- Required: No +- Type: string +- Default: `'ServiceManaged'` +- Allowed: + ```Bicep + [ + 'AzureKeyVault' + 'ServiceManaged' + ] + ``` + +### Parameter: `uri` + +The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required. + +- Required: No +- Type: string +- Default: `''` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed managed instance key. | +| `resourceGroupName` | string | The resource group of the deployed managed instance key. | +| `resourceId` | string | The resource ID of the deployed managed instance key. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/key/main.bicep b/avm/res/sql/managed-instance/key/main.bicep new file mode 100644 index 0000000000..d95ac84f4b --- /dev/null +++ b/avm/res/sql/managed-instance/key/main.bicep @@ -0,0 +1,47 @@ +metadata name = 'SQL Managed Instance Keys' +metadata description = 'This module deploys a SQL Managed Instance Key.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the key. Must follow the [__] pattern.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. The encryption protector type like "ServiceManaged", "AzureKeyVault".') +@allowed([ + 'AzureKeyVault' + 'ServiceManaged' +]) +param serverKeyType string = 'ServiceManaged' + +@description('Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required.') +param uri string = '' + +var splittedKeyUri = split(uri, '/') + +// if serverManaged, use serverManaged, if uri provided use concated uri value +// MUST match the pattern '__' +var serverKeyName = empty(uri) ? 'ServiceManaged' : '${split(splittedKeyUri[2], '.')[0]}_${splittedKeyUri[4]}_${splittedKeyUri[5]}' + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource key 'Microsoft.Sql/managedInstances/keys@2023-08-01-preview' = { + name: !empty(name) ? name : serverKeyName + parent: managedInstance + properties: { + serverKeyType: serverKeyType + uri: uri + } +} + +@description('The name of the deployed managed instance key.') +output name string = key.name + +@description('The resource ID of the deployed managed instance key.') +output resourceId string = key.id + +@description('The resource group of the deployed managed instance key.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/key/main.json b/avm/res/sql/managed-instance/key/main.json new file mode 100644 index 0000000000..403f395866 --- /dev/null +++ b/avm/res/sql/managed-instance/key/main.json @@ -0,0 +1,84 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "101283708334468532" + }, + "name": "SQL Managed Instance Keys", + "description": "This module deploys a SQL Managed Instance Key.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key. Must follow the [__] pattern." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "uri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required." + } + } + }, + "variables": { + "splittedKeyUri": "[split(parameters('uri'), '/')]", + "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/keys", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]", + "properties": { + "serverKeyType": "[parameters('serverKeyType')]", + "uri": "[parameters('uri')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance key." + }, + "value": "[if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance key." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/keys', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance key." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/main.bicep b/avm/res/sql/managed-instance/main.bicep new file mode 100644 index 0000000000..c180440c4d --- /dev/null +++ b/avm/res/sql/managed-instance/main.bicep @@ -0,0 +1,460 @@ +metadata name = 'SQL Managed Instances' +metadata description = 'This module deploys a SQL Managed Instance.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the SQL managed instance.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. The username used to establish jumpbox VMs.') +param administratorLogin string + +@description('Required. The password given to the admin user.') +@secure() +param administratorLoginPassword string + +@description('Required. The fully qualified resource ID of the subnet on which the SQL managed instance will be placed.') +param subnetResourceId string + +@description('Optional. The name of the SKU, typically, a letter + Number code, e.g. P3.') +param skuName string = 'GP_Gen5' + +@description('Optional. The tier or edition of the particular SKU, e.g. Basic, Premium.') +param skuTier string = 'GeneralPurpose' + +@description('Optional. Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only.') +param storageSizeInGB int = 32 + +@description('Optional. The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80.') +param vCores int = 4 + +@description('Optional. The license type. Possible values are \'LicenseIncluded\' (regular price inclusive of a new SQL license) and \'BasePrice\' (discounted AHB price for bringing your own SQL licenses).') +@allowed([ + 'LicenseIncluded' + 'BasePrice' +]) +param licenseType string = 'LicenseIncluded' + +@description('Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here.') +param hardwareFamily string = 'Gen5' + +@description('Optional. Whether or not multi-az is enabled.') +param zoneRedundant bool = false + +@description('Optional. Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal.') +@allowed([ + 'None' + 'SystemAssigned' +]) +param servicePrincipal string = 'None' + +@description('Optional. Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified.') +@allowed([ + 'Default' + 'PointInTimeRestore' +]) +param managedInstanceCreateMode string = 'Default' + +@description('Optional. The resource ID of another managed instance whose DNS zone this managed instance will share after creation.') +param dnsZonePartner string = '' + +@description('Optional. Collation of the managed instance.') +param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('Optional. Connection type used for connecting to the instance.') +@allowed([ + 'Proxy' + 'Redirect' + 'Default' +]) +param proxyOverride string = 'Proxy' + +@description('Optional. Whether or not the public data endpoint is enabled.') +param publicDataEndpointEnabled bool = false + +@description('Optional. ID of the timezone. Allowed values are timezones supported by Windows.') +param timezoneId string = 'UTC' + +@description('Optional. The resource ID of the instance pool this managed server belongs to.') +param instancePoolResourceId string = '' + +@description('Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database.') +param restorePointInTime string = '' + +@description('Optional. The resource identifier of the source managed instance associated with create operation of this instance.') +param sourceManagedInstanceId string = '' + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. The managed identity definition for this resource.') +param managedIdentities managedIdentitiesType + +@description('Conditional. The resource ID of a user assigned identity to be used by default. Required if "userAssignedIdentities" is not empty.') +param primaryUserAssignedIdentityId string = '' + +@description('Optional. Databases to create in this server.') +param databases array = [] + +@description('Optional. The vulnerability assessment configuration.') +param vulnerabilityAssessmentsObj object = {} + +@description('Optional. The security alert policy configuration.') +param securityAlertPoliciesObj object = {} + +@description('Optional. The keys to configure.') +param keys array = [] + +@description('Optional. The encryption protection configuration.') +param encryptionProtectorObj object = {} + +@description('Optional. The administrator configuration.') +param administratorsObj object = {} + +@description('Optional. Minimal TLS version allowed.') +@allowed([ + 'None' + '1.0' + '1.1' + '1.2' +]) +param minimalTlsVersion string = '1.2' + +@description('Optional. The storage account type used to store backups for this database.') +@allowed([ + 'Geo' + 'GeoZone' + 'Local' + 'Zone' +]) +param requestedBackupStorageRedundancy string = 'Geo' + +var formattedUserAssignedIdentities = reduce(map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, (cur, next) => union(cur, next)) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) ? { + type: (managedIdentities.?systemAssigned ?? false) ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null) + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null +} : null + + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'SQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec') + 'SQL Managed Instance Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d') + 'SQL Security Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3') + 'SQL Server Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437') + 'SqlDb Migration Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '189207d4-bb67-4208-a635-b06afe8b2c57') + 'SqlMI Migration Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d335eef-eee1-47fe-a9e0-53214eba8872') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.res.sql-managedinstance.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' = { + name: name + location: location + tags: tags + identity: identity + sku: { + name: skuName + tier: skuTier + family: hardwareFamily + } + + properties: { + managedInstanceCreateMode: managedInstanceCreateMode + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + subnetId: subnetResourceId + licenseType: licenseType + vCores: vCores + storageSizeInGB: storageSizeInGB + collation: collation + dnsZonePartner: !empty(dnsZonePartner) ? dnsZonePartner : null + publicDataEndpointEnabled: publicDataEndpointEnabled + sourceManagedInstanceId: !empty(sourceManagedInstanceId) ? sourceManagedInstanceId : null + restorePointInTime: !empty(restorePointInTime) ? restorePointInTime : null + proxyOverride: proxyOverride + timezoneId: timezoneId + instancePoolId: !empty(instancePoolResourceId) ? instancePoolResourceId : null + primaryUserAssignedIdentityId: !empty(primaryUserAssignedIdentityId) ? primaryUserAssignedIdentityId : null + requestedBackupStorageRedundancy: requestedBackupStorageRedundancy + zoneRedundant: zoneRedundant + servicePrincipal: { + type: servicePrincipal + } + minimalTlsVersion: minimalTlsVersion + } +} + +resource managedInstance_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot delete or modify the resource or child resources.' + } + scope: managedInstance +} + +resource managedInstance_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: managedInstance + } +] + +resource managedInstance_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (roleAssignments ?? []): { + name: guid(managedInstance.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) + ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] + : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: managedInstance + } +] + +module managedInstance_databases 'database/main.bicep' = [for (database, index) in databases: { + name: '${uniqueString(deployment().name, location)}-SqlMi-DB-${index}' + params: { + name: database.name + managedInstanceName: managedInstance.name + catalogCollation: contains(database, 'catalogCollation') ? database.catalogCollation : 'SQL_Latin1_General_CP1_CI_AS' + collation: contains(database, 'collation') ? database.collation : 'SQL_Latin1_General_CP1_CI_AS' + createMode: contains(database, 'createMode') ? database.createMode : 'Default' + diagnosticSettings: database.?diagnosticSettings + location: contains(database, 'location') ? database.location : managedInstance.location + lock: database.?lock ?? lock + longTermRetentionBackupResourceId: contains(database, 'longTermRetentionBackupResourceId') ? database.longTermRetentionBackupResourceId : '' + recoverableDatabaseId: contains(database, 'recoverableDatabaseId') ? database.recoverableDatabaseId : '' + restorableDroppedDatabaseId: contains(database, 'restorableDroppedDatabaseId') ? database.restorableDroppedDatabaseId : '' + restorePointInTime: contains(database, 'restorePointInTime') ? database.restorePointInTime : '' + sourceDatabaseId: contains(database, 'sourceDatabaseId') ? database.sourceDatabaseId : '' + storageContainerSasToken: contains(database, 'storageContainerSasToken') ? database.storageContainerSasToken : '' + storageContainerUri: contains(database, 'storageContainerUri') ? database.storageContainerUri : '' + tags: database.?tags ?? tags + backupShortTermRetentionPoliciesObj: contains(database, 'backupShortTermRetentionPolicies') ? database.backupShortTermRetentionPolicies : {} + backupLongTermRetentionPoliciesObj: contains(database, 'backupLongTermRetentionPolicies') ? database.backupLongTermRetentionPolicies : {} + } +}] + +module managedInstance_securityAlertPolicy 'security-alert-policy/main.bicep' = if (!empty(securityAlertPoliciesObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-SecAlertPol' + params: { + managedInstanceName: managedInstance.name + name: securityAlertPoliciesObj.name + emailAccountAdmins: contains(securityAlertPoliciesObj, 'emailAccountAdmins') ? securityAlertPoliciesObj.emailAccountAdmins : false + state: contains(securityAlertPoliciesObj, 'state') ? securityAlertPoliciesObj.state : 'Disabled' + } +} + +module managedInstance_vulnerabilityAssessment 'vulnerability-assessment/main.bicep' = if (!empty(vulnerabilityAssessmentsObj) && (managedIdentities.?systemAssigned ?? false)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-VulnAssessm' + params: { + managedInstanceName: managedInstance.name + name: vulnerabilityAssessmentsObj.name + recurringScansEmails: contains(vulnerabilityAssessmentsObj, 'recurringScansEmails') ? vulnerabilityAssessmentsObj.recurringScansEmails : [] + recurringScansEmailSubscriptionAdmins: contains(vulnerabilityAssessmentsObj, 'recurringScansEmailSubscriptionAdmins') ? vulnerabilityAssessmentsObj.recurringScansEmailSubscriptionAdmins : false + recurringScansIsEnabled: contains(vulnerabilityAssessmentsObj, 'recurringScansIsEnabled') ? vulnerabilityAssessmentsObj.recurringScansIsEnabled : false + storageAccountResourceId: vulnerabilityAssessmentsObj.storageAccountResourceId + useStorageAccountAccessKey: contains(vulnerabilityAssessmentsObj, 'useStorageAccountAccessKey') ? vulnerabilityAssessmentsObj.useStorageAccountAccessKey : false + createStorageRoleAssignment: contains(vulnerabilityAssessmentsObj, 'createStorageRoleAssignment') ? vulnerabilityAssessmentsObj.createStorageRoleAssignment : true + } + dependsOn: [ + managedInstance_securityAlertPolicy + ] +} + +module managedInstance_keys 'key/main.bicep' = [for (key, index) in keys: { + name: '${uniqueString(deployment().name, location)}-SqlMi-Key-${index}' + params: { + name: key.name + managedInstanceName: managedInstance.name + serverKeyType: contains(key, 'serverKeyType') ? key.serverKeyType : 'ServiceManaged' + uri: contains(key, 'uri') ? key.uri : '' + } +}] + +module managedInstance_encryptionProtector 'encryption-protector/main.bicep' = if (!empty(encryptionProtectorObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-EncryProtector' + params: { + managedInstanceName: managedInstance.name + serverKeyName: encryptionProtectorObj.serverKeyName + serverKeyType: contains(encryptionProtectorObj, 'serverKeyType') ? encryptionProtectorObj.serverKeyType : 'ServiceManaged' + autoRotationEnabled: contains(encryptionProtectorObj, 'autoRotationEnabled') ? encryptionProtectorObj.autoRotationEnabled : true + } + dependsOn: [ + managedInstance_keys + ] +} + +module managedInstance_administrator 'administrator/main.bicep' = if (!empty(administratorsObj)) { + name: '${uniqueString(deployment().name, location)}-SqlMi-Admin' + params: { + managedInstanceName: managedInstance.name + login: administratorsObj.name + sid: administratorsObj.sid + tenantId: contains(administratorsObj, 'tenantId') ? administratorsObj.tenantId : '' + } +} + +@description('The name of the deployed managed instance.') +output name string = managedInstance.name + +@description('The resource ID of the deployed managed instance.') +output resourceId string = managedInstance.id + +@description('The resource group of the deployed managed instance.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = managedInstance.?identity.?principalId ?? '' + +@description('The location the resource was deployed into.') +output location string = managedInstance.location + +// =============== // +// Definitions // +// =============== // + +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? + + @description('Optional. The resource ID(s) to assign to the resource.') + userAssignedResourceIds: string[]? +}? + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @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? + + @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? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/sql/managed-instance/main.json b/avm/res/sql/managed-instance/main.json new file mode 100644 index 0000000000..1786333be1 --- /dev/null +++ b/avm/res/sql/managed-instance/main.json @@ -0,0 +1,1986 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "9634979761136417752" + }, + "name": "SQL Managed Instances", + "description": "This module deploys a SQL Managed Instance.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "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'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "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\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "administratorLogin": { + "type": "string", + "metadata": { + "description": "Required. The username used to establish jumpbox VMs." + } + }, + "administratorLoginPassword": { + "type": "securestring", + "metadata": { + "description": "Required. The password given to the admin user." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The fully qualified resource ID of the subnet on which the SQL managed instance will be placed." + } + }, + "skuName": { + "type": "string", + "defaultValue": "GP_Gen5", + "metadata": { + "description": "Optional. The name of the SKU, typically, a letter + Number code, e.g. P3." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "GeneralPurpose", + "metadata": { + "description": "Optional. The tier or edition of the particular SKU, e.g. Basic, Premium." + } + }, + "storageSizeInGB": { + "type": "int", + "defaultValue": 32, + "metadata": { + "description": "Optional. Storage size in GB. Minimum value: 32. Maximum value: 8192. Increments of 32 GB allowed only." + } + }, + "vCores": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The number of vCores. Allowed values: 8, 16, 24, 32, 40, 64, 80." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "LicenseIncluded", + "allowedValues": [ + "LicenseIncluded", + "BasePrice" + ], + "metadata": { + "description": "Optional. The license type. Possible values are 'LicenseIncluded' (regular price inclusive of a new SQL license) and 'BasePrice' (discounted AHB price for bringing your own SQL licenses)." + } + }, + "hardwareFamily": { + "type": "string", + "defaultValue": "Gen5", + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether or not multi-az is enabled." + } + }, + "servicePrincipal": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "None", + "SystemAssigned" + ], + "metadata": { + "description": "Optional. Service principal type. If using AD Authentication and applying Admin, must be set to `SystemAssigned`. Then Global Admin must allow Reader access to Azure AD for the Service Principal." + } + }, + "managedInstanceCreateMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "PointInTimeRestore" + ], + "metadata": { + "description": "Optional. Specifies the mode of database creation. Default: Regular instance creation. Restore: Creates an instance by restoring a set of backups to specific point in time. RestorePointInTime and SourceManagedInstanceId must be specified." + } + }, + "dnsZonePartner": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of another managed instance whose DNS zone this managed instance will share after creation." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance." + } + }, + "proxyOverride": { + "type": "string", + "defaultValue": "Proxy", + "allowedValues": [ + "Proxy", + "Redirect", + "Default" + ], + "metadata": { + "description": "Optional. Connection type used for connecting to the instance." + } + }, + "publicDataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether or not the public data endpoint is enabled." + } + }, + "timezoneId": { + "type": "string", + "defaultValue": "UTC", + "metadata": { + "description": "Optional. ID of the timezone. Allowed values are timezones supported by Windows." + } + }, + "instancePoolResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the instance pool this managed server belongs to." + } + }, + "restorePointInTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database." + } + }, + "sourceManagedInstanceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource identifier of the source managed instance associated with create operation of this instance." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "primaryUserAssignedIdentityId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of a user assigned identity to be used by default. Required if \"userAssignedIdentities\" is not empty." + } + }, + "databases": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Databases to create in this server." + } + }, + "vulnerabilityAssessmentsObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The vulnerability assessment configuration." + } + }, + "securityAlertPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The security alert policy configuration." + } + }, + "keys": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The keys to configure." + } + }, + "encryptionProtectorObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The encryption protection configuration." + } + }, + "administratorsObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The administrator configuration." + } + }, + "minimalTlsVersion": { + "type": "string", + "defaultValue": "1.2", + "allowedValues": [ + "None", + "1.0", + "1.1", + "1.2" + ], + "metadata": { + "description": "Optional. Minimal TLS version allowed." + } + }, + "requestedBackupStorageRedundancy": { + "type": "string", + "defaultValue": "Geo", + "allowedValues": [ + "Geo", + "GeoZone", + "Local", + "Zone" + ], + "metadata": { + "description": "Optional. The storage account type used to store backups for this database." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reservation Purchaser": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "SQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec')]", + "SQL Managed Instance Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d')]", + "SQL Security Manager": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3')]", + "SQL Server Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437')]", + "SqlDb Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '189207d4-bb67-4208-a635-b06afe8b2c57')]", + "SqlMI Migration Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1d335eef-eee1-47fe-a9e0-53214eba8872')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.sql-managedinstance.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "managedInstance": { + "type": "Microsoft.Sql/managedInstances", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]", + "family": "[parameters('hardwareFamily')]" + }, + "properties": { + "managedInstanceCreateMode": "[parameters('managedInstanceCreateMode')]", + "administratorLogin": "[parameters('administratorLogin')]", + "administratorLoginPassword": "[parameters('administratorLoginPassword')]", + "subnetId": "[parameters('subnetResourceId')]", + "licenseType": "[parameters('licenseType')]", + "vCores": "[parameters('vCores')]", + "storageSizeInGB": "[parameters('storageSizeInGB')]", + "collation": "[parameters('collation')]", + "dnsZonePartner": "[if(not(empty(parameters('dnsZonePartner'))), parameters('dnsZonePartner'), null())]", + "publicDataEndpointEnabled": "[parameters('publicDataEndpointEnabled')]", + "sourceManagedInstanceId": "[if(not(empty(parameters('sourceManagedInstanceId'))), parameters('sourceManagedInstanceId'), null())]", + "restorePointInTime": "[if(not(empty(parameters('restorePointInTime'))), parameters('restorePointInTime'), null())]", + "proxyOverride": "[parameters('proxyOverride')]", + "timezoneId": "[parameters('timezoneId')]", + "instancePoolId": "[if(not(empty(parameters('instancePoolResourceId'))), parameters('instancePoolResourceId'), null())]", + "primaryUserAssignedIdentityId": "[if(not(empty(parameters('primaryUserAssignedIdentityId'))), parameters('primaryUserAssignedIdentityId'), null())]", + "requestedBackupStorageRedundancy": "[parameters('requestedBackupStorageRedundancy')]", + "zoneRedundant": "[parameters('zoneRedundant')]", + "servicePrincipal": { + "type": "[parameters('servicePrincipal')]" + }, + "minimalTlsVersion": "[parameters('minimalTlsVersion')]" + } + }, + "managedInstance_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_diagnosticSettings": { + "copy": { + "name": "managedInstance_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/managedInstances/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_roleAssignments": { + "copy": { + "name": "managedInstance_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Sql/managedInstances', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_databases": { + "copy": { + "name": "managedInstance_databases", + "count": "[length(parameters('databases'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-DB-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('databases')[copyIndex()].name]" + }, + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "catalogCollation": "[if(contains(parameters('databases')[copyIndex()], 'catalogCollation'), createObject('value', parameters('databases')[copyIndex()].catalogCollation), createObject('value', 'SQL_Latin1_General_CP1_CI_AS'))]", + "collation": "[if(contains(parameters('databases')[copyIndex()], 'collation'), createObject('value', parameters('databases')[copyIndex()].collation), createObject('value', 'SQL_Latin1_General_CP1_CI_AS'))]", + "createMode": "[if(contains(parameters('databases')[copyIndex()], 'createMode'), createObject('value', parameters('databases')[copyIndex()].createMode), createObject('value', 'Default'))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('databases')[copyIndex()], 'diagnosticSettings')]" + }, + "location": "[if(contains(parameters('databases')[copyIndex()], 'location'), createObject('value', parameters('databases')[copyIndex()].location), createObject('value', reference('managedInstance', '2023-08-01-preview', 'full').location))]", + "lock": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'lock'), parameters('lock'))]" + }, + "longTermRetentionBackupResourceId": "[if(contains(parameters('databases')[copyIndex()], 'longTermRetentionBackupResourceId'), createObject('value', parameters('databases')[copyIndex()].longTermRetentionBackupResourceId), createObject('value', ''))]", + "recoverableDatabaseId": "[if(contains(parameters('databases')[copyIndex()], 'recoverableDatabaseId'), createObject('value', parameters('databases')[copyIndex()].recoverableDatabaseId), createObject('value', ''))]", + "restorableDroppedDatabaseId": "[if(contains(parameters('databases')[copyIndex()], 'restorableDroppedDatabaseId'), createObject('value', parameters('databases')[copyIndex()].restorableDroppedDatabaseId), createObject('value', ''))]", + "restorePointInTime": "[if(contains(parameters('databases')[copyIndex()], 'restorePointInTime'), createObject('value', parameters('databases')[copyIndex()].restorePointInTime), createObject('value', ''))]", + "sourceDatabaseId": "[if(contains(parameters('databases')[copyIndex()], 'sourceDatabaseId'), createObject('value', parameters('databases')[copyIndex()].sourceDatabaseId), createObject('value', ''))]", + "storageContainerSasToken": "[if(contains(parameters('databases')[copyIndex()], 'storageContainerSasToken'), createObject('value', parameters('databases')[copyIndex()].storageContainerSasToken), createObject('value', ''))]", + "storageContainerUri": "[if(contains(parameters('databases')[copyIndex()], 'storageContainerUri'), createObject('value', parameters('databases')[copyIndex()].storageContainerUri), createObject('value', ''))]", + "tags": { + "value": "[coalesce(tryGet(parameters('databases')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "backupShortTermRetentionPoliciesObj": "[if(contains(parameters('databases')[copyIndex()], 'backupShortTermRetentionPolicies'), createObject('value', parameters('databases')[copyIndex()].backupShortTermRetentionPolicies), createObject('value', createObject()))]", + "backupLongTermRetentionPoliciesObj": "[if(contains(parameters('databases')[copyIndex()], 'backupLongTermRetentionPolicies'), createObject('value', parameters('databases')[copyIndex()].backupLongTermRetentionPolicies), createObject('value', createObject()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15831945430809011826" + }, + "name": "SQL Managed Instance Databases", + "description": "This module deploys a SQL Managed Instance Database.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to 'AllLogs' to collect all logs." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to '' to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance database." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "collation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance database." + } + }, + "catalogCollation": { + "type": "string", + "defaultValue": "SQL_Latin1_General_CP1_CI_AS", + "metadata": { + "description": "Optional. Collation of the managed instance." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "RestoreExternalBackup", + "PointInTimeRestore", + "Recovery", + "RestoreLongTermRetentionBackup" + ], + "metadata": { + "description": "Optional. Managed database create mode. PointInTimeRestore: Create a database by restoring a point in time backup of an existing database. SourceDatabaseName, SourceManagedInstanceName and PointInTime must be specified. RestoreExternalBackup: Create a database by restoring from external backup files. Collation, StorageContainerUri and StorageContainerSasToken must be specified. Recovery: Creates a database by restoring a geo-replicated backup. RecoverableDatabaseId must be specified as the recoverable database resource ID to restore. RestoreLongTermRetentionBackup: Create a database by restoring from a long term retention backup (longTermRetentionBackupResourceId required)." + } + }, + "sourceDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the source database associated with create operation of this database. Required if createMode is PointInTimeRestore." + } + }, + "restorePointInTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the point in time (ISO8601 format) of the source database that will be restored to create the new database. Required if createMode is PointInTimeRestore." + } + }, + "restorableDroppedDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The restorable dropped database resource ID to restore when creating this database." + } + }, + "storageContainerUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the uri of the storage container where backups for this restore are stored. Required if createMode is RestoreExternalBackup." + } + }, + "storageContainerSasToken": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Conditional. Specifies the storage container sas token. Required if createMode is RestoreExternalBackup." + } + }, + "recoverableDatabaseId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource identifier of the recoverable database associated with create operation of this database. Required if createMode is Recovery." + } + }, + "longTermRetentionBackupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of the Long Term Retention backup to be used for restore of this managed database. Required if createMode is RestoreLongTermRetentionBackup." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "backupShortTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup short term retention policy definition." + } + }, + "backupLongTermRetentionPoliciesObj": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the backup long term retention policy definition." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "managedInstance": { + "existing": true, + "type": "Microsoft.Sql/managedInstances", + "apiVersion": "2023-08-01-preview", + "name": "[parameters('managedInstanceName')]" + }, + "database": { + "type": "Microsoft.Sql/managedInstances/databases", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "collation": "[if(empty(parameters('collation')), null(), parameters('collation'))]", + "restorePointInTime": "[if(empty(parameters('restorePointInTime')), null(), parameters('restorePointInTime'))]", + "catalogCollation": "[if(empty(parameters('catalogCollation')), null(), parameters('catalogCollation'))]", + "createMode": "[if(empty(parameters('createMode')), null(), parameters('createMode'))]", + "storageContainerUri": "[if(empty(parameters('storageContainerUri')), null(), parameters('storageContainerUri'))]", + "sourceDatabaseId": "[if(empty(parameters('sourceDatabaseId')), null(), parameters('sourceDatabaseId'))]", + "restorableDroppedDatabaseId": "[if(empty(parameters('restorableDroppedDatabaseId')), null(), parameters('restorableDroppedDatabaseId'))]", + "storageContainerSasToken": "[if(empty(parameters('storageContainerSasToken')), null(), parameters('storageContainerSasToken'))]", + "recoverableDatabaseId": "[if(empty(parameters('recoverableDatabaseId')), null(), parameters('recoverableDatabaseId'))]", + "longTermRetentionBackupResourceId": "[if(empty(parameters('longTermRetentionBackupResourceId')), null(), parameters('longTermRetentionBackupResourceId'))]" + }, + "dependsOn": [ + "managedInstance" + ] + }, + "database_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "database" + ] + }, + "database_diagnosticSettings": { + "copy": { + "name": "database_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Sql/managedInstances/{0}/databases/{1}', parameters('managedInstanceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "database" + ] + }, + "database_backupShortTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupShortTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupShortTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupShortTermRetentionPoliciesObj').name]" + }, + "retentionDays": "[if(contains(parameters('backupShortTermRetentionPoliciesObj'), 'retentionDays'), createObject('value', parameters('backupShortTermRetentionPoliciesObj').retentionDays), createObject('value', 35))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "4816864047763535590" + }, + "name": "SQL Managed Instance Database Backup Short-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Short-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Short Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "retentionDays": { + "type": "int", + "defaultValue": 35, + "metadata": { + "description": "Optional. The backup retention period in days. This is how many days Point-in-Time Restore will be supported." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "retentionDays": "[parameters('retentionDays')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup short-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup short-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupShortTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup short-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + }, + "database_backupLongTermRetentionPolicy": { + "condition": "[not(empty(parameters('backupLongTermRetentionPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-BackupLongTRetPol', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('managedInstanceName')]" + }, + "databaseName": { + "value": "[last(split(parameters('name'), '/'))]" + }, + "name": { + "value": "[parameters('backupLongTermRetentionPoliciesObj').name]" + }, + "weekOfYear": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weekOfYear'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weekOfYear), createObject('value', 5))]", + "weeklyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'weeklyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').weeklyRetention), createObject('value', 'P1M'))]", + "monthlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'monthlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').monthlyRetention), createObject('value', 'P1Y'))]", + "yearlyRetention": "[if(contains(parameters('backupLongTermRetentionPoliciesObj'), 'yearlyRetention'), createObject('value', parameters('backupLongTermRetentionPoliciesObj').yearlyRetention), createObject('value', 'P5Y'))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "2315344113299493188" + }, + "name": "SQL Managed Instance Database Backup Long-Term Retention Policies", + "description": "This module deploys a SQL Managed Instance Database Backup Long-Term Retention Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Long Term Retention backup policy. For example \"default\"." + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance database. Required if the template is used in a standalone deployment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed instance. Required if the template is used in a standalone deployment." + } + }, + "weekOfYear": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The week of year to take the yearly backup in an ISO 8601 format." + } + }, + "weeklyRetention": { + "type": "string", + "defaultValue": "P1M", + "metadata": { + "description": "Optional. The weekly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "monthlyRetention": { + "type": "string", + "defaultValue": "P1Y", + "metadata": { + "description": "Optional. The monthly retention policy for an LTR backup in an ISO 8601 format." + } + }, + "yearlyRetention": { + "type": "string", + "defaultValue": "P5Y", + "metadata": { + "description": "Optional. The yearly retention policy for an LTR backup in an ISO 8601 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}/{2}', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]", + "properties": { + "monthlyRetention": "[parameters('monthlyRetention')]", + "weeklyRetention": "[parameters('weeklyRetention')]", + "weekOfYear": "[parameters('weekOfYear')]", + "yearlyRetention": "[parameters('yearlyRetention')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database backup long-term retention policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database backup long-term retention policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases/backupLongTermRetentionPolicies', parameters('managedInstanceName'), parameters('databaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed database backup long-term retention policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "database" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed database." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/databases', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the database was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('database', '2023-08-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_securityAlertPolicy": { + "condition": "[not(empty(parameters('securityAlertPoliciesObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-SecAlertPol', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('securityAlertPoliciesObj').name]" + }, + "emailAccountAdmins": "[if(contains(parameters('securityAlertPoliciesObj'), 'emailAccountAdmins'), createObject('value', parameters('securityAlertPoliciesObj').emailAccountAdmins), createObject('value', false()))]", + "state": "[if(contains(parameters('securityAlertPoliciesObj'), 'state'), createObject('value', parameters('securityAlertPoliciesObj').state), createObject('value', 'Disabled'))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "16936228680401372562" + }, + "name": "SQL Managed Instance Security Alert Policies", + "description": "This module deploys a SQL Managed Instance Security Alert Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security alert policy." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "state": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided." + } + }, + "emailAccountAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/securityAlertPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "state": "[parameters('state')]", + "emailAccountAdmins": "[parameters('emailAccountAdmins')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed security alert policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed security alert policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/securityAlertPolicies', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed security alert policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_vulnerabilityAssessment": { + "condition": "[and(not(empty(parameters('vulnerabilityAssessmentsObj'))), coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-VulnAssessm', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('vulnerabilityAssessmentsObj').name]" + }, + "recurringScansEmails": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmails'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansEmails), createObject('value', createArray()))]", + "recurringScansEmailSubscriptionAdmins": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansEmailSubscriptionAdmins'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansEmailSubscriptionAdmins), createObject('value', false()))]", + "recurringScansIsEnabled": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'recurringScansIsEnabled'), createObject('value', parameters('vulnerabilityAssessmentsObj').recurringScansIsEnabled), createObject('value', false()))]", + "storageAccountResourceId": { + "value": "[parameters('vulnerabilityAssessmentsObj').storageAccountResourceId]" + }, + "useStorageAccountAccessKey": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'useStorageAccountAccessKey'), createObject('value', parameters('vulnerabilityAssessmentsObj').useStorageAccountAccessKey), createObject('value', false()))]", + "createStorageRoleAssignment": "[if(contains(parameters('vulnerabilityAssessmentsObj'), 'createStorageRoleAssignment'), createObject('value', parameters('vulnerabilityAssessmentsObj').createStorageRoleAssignment), createObject('value', true()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15455405847587423486" + }, + "name": "SQL Managed Instance Vulnerability Assessments", + "description": "This module deploys a SQL Managed Instance Vulnerability Assessment.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the vulnerability assessment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "recurringScansIsEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Recurring scans state." + } + }, + "recurringScansEmailSubscriptionAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + }, + "recurringScansEmails": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies an array of email addresses to which the scan notification is sent." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. A blob storage to hold the scan results." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." + } + }, + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/vulnerabilityAssessments", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", + "storageAccountAccessKey": "[if(parameters('useStorageAccountAccessKey'), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", + "recurringScans": { + "isEnabled": "[parameters('recurringScansIsEnabled')]", + "emailSubscriptionAdmins": "[parameters('recurringScansEmailSubscriptionAdmins')]", + "emails": "[parameters('recurringScansEmails')]" + } + } + }, + { + "condition": "[and(not(parameters('useStorageAccountAccessKey')), parameters('createStorageRoleAssignment'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sbdc-rbac', parameters('managedInstanceName'))]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Sql/managedInstances', parameters('managedInstanceName')), '2023-08-01-preview', 'full').identity.principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18021215853157074333" + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. Storage account name." + } + }, + "managedInstanceIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "Required. Managed Identity Principal ID." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed vulnerability assessment." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed vulnerability assessment." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/vulnerabilityAssessments', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed vulnerability assessment." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance", + "managedInstance_securityAlertPolicy" + ] + }, + "managedInstance_keys": { + "copy": { + "name": "managedInstance_keys", + "count": "[length(parameters('keys'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-Key-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('keys')[copyIndex()].name]" + }, + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "serverKeyType": "[if(contains(parameters('keys')[copyIndex()], 'serverKeyType'), createObject('value', parameters('keys')[copyIndex()].serverKeyType), createObject('value', 'ServiceManaged'))]", + "uri": "[if(contains(parameters('keys')[copyIndex()], 'uri'), createObject('value', parameters('keys')[copyIndex()].uri), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "101283708334468532" + }, + "name": "SQL Managed Instance Keys", + "description": "This module deploys a SQL Managed Instance Key.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the key. Must follow the [__] pattern." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "uri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The URI of the key. If the ServerKeyType is AzureKeyVault, then either the URI or the keyVaultName/keyName combination is required." + } + } + }, + "variables": { + "splittedKeyUri": "[split(parameters('uri'), '/')]", + "serverKeyName": "[if(empty(parameters('uri')), 'ServiceManaged', format('{0}_{1}_{2}', split(variables('splittedKeyUri')[2], '.')[0], variables('splittedKeyUri')[4], variables('splittedKeyUri')[5]))]" + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/keys", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]", + "properties": { + "serverKeyType": "[parameters('serverKeyType')]", + "uri": "[parameters('uri')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance key." + }, + "value": "[if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance key." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/keys', parameters('managedInstanceName'), if(not(empty(parameters('name'))), parameters('name'), variables('serverKeyName')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance key." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + }, + "managedInstance_encryptionProtector": { + "condition": "[not(empty(parameters('encryptionProtectorObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-EncryProtector', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "serverKeyName": { + "value": "[parameters('encryptionProtectorObj').serverKeyName]" + }, + "serverKeyType": "[if(contains(parameters('encryptionProtectorObj'), 'serverKeyType'), createObject('value', parameters('encryptionProtectorObj').serverKeyType), createObject('value', 'ServiceManaged'))]", + "autoRotationEnabled": "[if(contains(parameters('encryptionProtectorObj'), 'autoRotationEnabled'), createObject('value', parameters('encryptionProtectorObj').autoRotationEnabled), createObject('value', true()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "13463643567330956322" + }, + "name": "SQL Managed Instance Encryption Protector", + "description": "This module deploys a SQL Managed Instance Encryption Protector.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "serverKeyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the SQL managed instance key." + } + }, + "serverKeyType": { + "type": "string", + "defaultValue": "ServiceManaged", + "allowedValues": [ + "AzureKeyVault", + "ServiceManaged" + ], + "metadata": { + "description": "Optional. The encryption protector type like \"ServiceManaged\", \"AzureKeyVault\"." + } + }, + "autoRotationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Key auto rotation opt-in flag." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/encryptionProtector", + "apiVersion": "2022-05-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'current')]", + "properties": { + "autoRotationEnabled": "[parameters('autoRotationEnabled')]", + "serverKeyName": "[parameters('serverKeyName')]", + "serverKeyType": "[parameters('serverKeyType')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance encryption protector." + }, + "value": "current" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance encryption protector." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/encryptionProtector', parameters('managedInstanceName'), 'current')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance encryption protector." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance", + "managedInstance_keys" + ] + }, + "managedInstance_administrator": { + "condition": "[not(empty(parameters('administratorsObj')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-SqlMi-Admin', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedInstanceName": { + "value": "[parameters('name')]" + }, + "login": { + "value": "[parameters('administratorsObj').name]" + }, + "sid": { + "value": "[parameters('administratorsObj').sid]" + }, + "tenantId": "[if(contains(parameters('administratorsObj'), 'tenantId'), createObject('value', parameters('administratorsObj').tenantId), createObject('value', ''))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "14195697460339552085" + }, + "name": "SQL Managed Instances Administrator", + "description": "This module deploys a SQL Managed Instance Administrator.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "login": { + "type": "string", + "metadata": { + "description": "Required. Login name of the managed instance administrator." + } + }, + "sid": { + "type": "string", + "metadata": { + "description": "Required. SID (object ID) of the managed instance administrator." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Tenant ID of the managed instance administrator." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/administrators", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), 'ActiveDirectory')]", + "properties": { + "administratorType": "ActiveDirectory", + "login": "[parameters('login')]", + "sid": "[parameters('sid')]", + "tenantId": "[parameters('tenantId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance administrator." + }, + "value": "ActiveDirectory" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance administrator." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/administrators', parameters('managedInstanceName'), 'ActiveDirectory')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance administrator." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedInstance" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed managed instance." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed managed instance." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed managed instance." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('managedInstance', '2023-08-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('managedInstance', '2023-08-01-preview', 'full').location]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/security-alert-policy/README.md b/avm/res/sql/managed-instance/security-alert-policy/README.md new file mode 100644 index 0000000000..29b532c3a8 --- /dev/null +++ b/avm/res/sql/managed-instance/security-alert-policy/README.md @@ -0,0 +1,92 @@ +# SQL Managed Instance Security Alert Policies `[Microsoft.Sql/managedInstances/securityAlertPolicies]` + +This module deploys a SQL Managed Instance Security Alert Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/managedInstances/securityAlertPolicies` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/securityAlertPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the security alert policy. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`emailAccountAdmins`](#parameter-emailaccountadmins) | bool | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| [`state`](#parameter-state) | string | Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided. | + +### Parameter: `name` + +The name of the security alert policy. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `emailAccountAdmins` + +Specifies that the schedule scan notification will be is sent to the subscription administrators. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `state` + +Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided. + +- Required: No +- Type: string +- Default: `'Disabled'` +- Allowed: + ```Bicep + [ + 'Disabled' + 'Enabled' + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed security alert policy. | +| `resourceGroupName` | string | The resource group of the deployed security alert policy. | +| `resourceId` | string | The resource ID of the deployed security alert policy. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/security-alert-policy/main.bicep b/avm/res/sql/managed-instance/security-alert-policy/main.bicep new file mode 100644 index 0000000000..1928a343fe --- /dev/null +++ b/avm/res/sql/managed-instance/security-alert-policy/main.bicep @@ -0,0 +1,41 @@ +metadata name = 'SQL Managed Instance Security Alert Policies' +metadata description = 'This module deploys a SQL Managed Instance Security Alert Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the security alert policy.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param state string = 'Disabled' + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param emailAccountAdmins bool = false + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +resource securityAlertPolicy 'Microsoft.Sql/managedInstances/securityAlertPolicies@2023-08-01-preview' = { + name: name + parent: managedInstance + properties: { + state: state + emailAccountAdmins: emailAccountAdmins + } +} + +@description('The name of the deployed security alert policy.') +output name string = securityAlertPolicy.name + +@description('The resource ID of the deployed security alert policy.') +output resourceId string = securityAlertPolicy.id + +@description('The resource group of the deployed security alert policy.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/security-alert-policy/main.json b/avm/res/sql/managed-instance/security-alert-policy/main.json new file mode 100644 index 0000000000..021521bfa3 --- /dev/null +++ b/avm/res/sql/managed-instance/security-alert-policy/main.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "16936228680401372562" + }, + "name": "SQL Managed Instance Security Alert Policies", + "description": "This module deploys a SQL Managed Instance Security Alert Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security alert policy." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "state": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Enables advanced data security features, like recuring vulnerability assesment scans and ATP. If enabled, storage account must be provided." + } + }, + "emailAccountAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/securityAlertPolicies", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "state": "[parameters('state')]", + "emailAccountAdmins": "[parameters('emailAccountAdmins')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed security alert policy." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed security alert policy." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/securityAlertPolicies', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed security alert policy." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..b1f97e3ddf --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,288 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.westeurope' + properties: { + addressPrefix: 'AzureCloud.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.northeurope' + properties: { + addressPrefix: 'AzureCloud.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westeurope' + properties: { + addressPrefix: 'Storage.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.northeurope' + properties: { + addressPrefix: 'Storage.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.westeurope' + properties: { + addressPrefix: 'EventHub.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.northeurope' + properties: { + addressPrefix: 'EventHub.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..e6eda07e9e --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,64 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.managedinstances-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sqlmimin' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}-${serviceShort}' + location: resourceLocation + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } +}] diff --git a/avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..20398fd372 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/max/dependencies.bicep @@ -0,0 +1,326 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.${location}' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.${location}' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.${location}' + properties: { + addressPrefix: 'EventHub.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The URL of the created Key Vault Encryption Key.') +output keyVaultEncryptionKeyUrl string = keyVault::key.properties.keyUriWithVersion + +@description('The name of the created Key Vault Encryption Key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The name of the created Key Vault.') +output keyVaultName string = keyVault.name diff --git a/avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..b45ce3cbb7 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/max/main.test.bicep @@ -0,0 +1,189 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.managedinstances-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sqlmimax' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep${namePrefix}kv${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: toLower('dep${namePrefix}x${serviceShort}01') + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + name: '${namePrefix}-${serviceShort}-db-001' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + logCategoriesAndGroups:[ + { + categoryGroup: 'allLogs' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + uri: nestedDependencies.outputs.keyVaultEncryptionKeyUrl + } + ] + licenseType: 'LicenseIncluded' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + roleAssignments: [ + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +}] diff --git a/avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep new file mode 100644 index 0000000000..d06ccfa76e --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/dependencies.bicep @@ -0,0 +1,386 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-aad-out-${addressPrefixString}-v11' + properties: { + description: 'Allow communication with Azure Active Directory over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureActiveDirectory' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-onedsc-out-${addressPrefixString}-v11' + properties: { + description: 'Allow communication with the One DS Collector over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'OneDsCollector' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'mi-strg-p-out-${addressPrefixString}-v11' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'Storage.eastus' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + { + name: 'mi-strg-s-out-${addressPrefixString}-v11' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'Storage.westus' + access: 'Allow' + priority: 105 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.eastus' + properties: { + addressPrefix: 'Storage.eastus' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westus' + properties: { + addressPrefix: 'Storage.westus' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + 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-AzureCloud.westeurope' + properties: { + addressPrefix: 'AzureCloud.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.northeurope' + properties: { + addressPrefix: 'AzureCloud.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.westeurope' + properties: { + addressPrefix: 'Storage.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.northeurope' + properties: { + addressPrefix: 'Storage.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.westeurope' + properties: { + addressPrefix: 'EventHub.westeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.northeurope' + properties: { + addressPrefix: 'EventHub.northeurope' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + } +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep new file mode 100644 index 0000000000..a51971ca80 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/vulnAssm/main.test.bicep @@ -0,0 +1,90 @@ +targetScope = 'subscription' + +metadata name = 'With vulnerability assessment' +metadata description = 'This instance deploys the module with a vulnerability assessment.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.managedinstances-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sqlmivln' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + storageAccountName: toLower('dep${namePrefix}v${serviceShort}01') + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + managedIdentities: { + systemAssigned: true + } + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + useStorageAccountAccessKey: false + createStorageRoleAssignment: true + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +}] diff --git a/avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..20398fd372 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,326 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +@description('Required. The name of the Route Table to create.') +param routeTableName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' +var addressPrefixString = replace(replace(addressPrefix, '.', '-'), '/', '-') + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-04-01' = { + name: networkSecurityGroupName + location: location + properties: { + securityRules: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-sqlmgmt-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI provisioning Control Plane Deployment and Authentication Service' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'SqlManagement' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1438' + '1440' + '1452' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corpsaw-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetSaw' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + '1440' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-corppublic-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI Supportability through Corpnet ranges' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: 'CorpNetPublic' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 102 + direction: 'Inbound' + destinationPortRanges: [ + '9000' + '9003' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-healthprobe-in-${addressPrefixString}-v10' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 103 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-in-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 104 + direction: 'Inbound' + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-services-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI services outbound traffic over https' + protocol: 'Tcp' + sourcePortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + destinationPortRanges: [ + '443' + '12000' + ] + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-internal-out-${addressPrefixString}-v10' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: addressPrefix + destinationAddressPrefix: addressPrefix + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'Microsoft.Sql-managedInstances_UseOnly_subnet-${addressPrefixString}-to-vnetlocal' + properties: { + addressPrefix: addressPrefix + nextHopType: 'VnetLocal' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage' + properties: { + addressPrefix: 'Storage' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-SqlManagement' + properties: { + addressPrefix: 'SqlManagement' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureMonitor' + properties: { + addressPrefix: 'AzureMonitor' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetSaw' + properties: { + addressPrefix: 'CorpNetSaw' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-CorpNetPublic' + properties: { + addressPrefix: 'CorpNetPublic' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureActiveDirectory' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-AzureCloud.${location}' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-Storage.${location}' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + { + name: 'Microsoft.Sql-managedInstances_UseOnly_mi-EventHub.${location}' + properties: { + addressPrefix: 'EventHub.${location}' + nextHopType: 'Internet' + hasBgpOverride: false + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'ManagedInstance' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + routeTable: { + id: routeTable.id + } + networkSecurityGroup: { + id: networkSecurityGroup.id + } + delegations: [ + { + name: 'managedInstanceDelegation' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + tenantId: tenant().tenantId + enablePurgeProtection: true + softDeleteRetentionInDays: 7 + enabledForTemplateDeployment: true + enabledForDiskEncryption: true + enabledForDeployment: true + enableRbacAuthorization: true + accessPolicies: [] + } + + resource key 'keys@2022-07-01' = { + name: 'keyEncryptionKey' + properties: { + kty: 'RSA' + } + } +} + +resource keyPermissions 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${keyVault::key.id}-${location}-${managedIdentity.id}-Key-Reader-RoleAssignment') + scope: keyVault::key + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User + principalType: 'ServicePrincipal' + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The URL of the created Key Vault Encryption Key.') +output keyVaultEncryptionKeyUrl string = keyVault::key.properties.keyUriWithVersion + +@description('The name of the created Key Vault Encryption Key.') +output keyVaultKeyName string = keyVault::key.name + +@description('The name of the created Key Vault.') +output keyVaultName string = keyVault.name diff --git a/avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..7cbd5f19b5 --- /dev/null +++ b/avm/res/sql/managed-instance/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,172 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.managedinstances-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sqlmiwaf' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // Adding base time to make the name unique as purge protection must be enabled (but may not be longer than 24 characters total) + keyVaultName: 'dep${namePrefix}kv${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + networkSecurityGroupName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + location: resourceLocation + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: toLower('dep${namePrefix}wf${serviceShort}01') + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [for iteration in [ 'init', 'idem' ]: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}-${serviceShort}' + administratorLogin: 'adminUserName' + administratorLoginPassword: password + subnetResourceId: nestedDependencies.outputs.subnetResourceId + collation: 'SQL_Latin1_General_CP1_CI_AS' + databases: [ + { + backupLongTermRetentionPolicies: { + name: 'default' + } + backupShortTermRetentionPolicies: { + name: 'default' + } + name: '${namePrefix}-${serviceShort}-db-001' + diagnosticSettings: [ + { + name: 'customSetting' + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + dnsZonePartner: '' + encryptionProtectorObj: { + serverKeyName: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + } + hardwareFamily: 'Gen5' + keys: [ + { + name: '${nestedDependencies.outputs.keyVaultName}_${nestedDependencies.outputs.keyVaultKeyName}_${last(split(nestedDependencies.outputs.keyVaultEncryptionKeyUrl, '/'))}' + serverKeyType: 'AzureKeyVault' + uri: nestedDependencies.outputs.keyVaultEncryptionKeyUrl + } + ] + licenseType: 'LicenseIncluded' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + primaryUserAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + proxyOverride: 'Proxy' + publicDataEndpointEnabled: false + securityAlertPoliciesObj: { + emailAccountAdmins: true + name: 'default' + state: 'Enabled' + } + servicePrincipal: 'SystemAssigned' + skuName: 'GP_Gen5' + skuTier: 'GeneralPurpose' + storageSizeInGB: 32 + managedIdentities: { + systemAssigned: true + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } + timezoneId: 'UTC' + vCores: 4 + vulnerabilityAssessmentsObj: { + emailSubscriptionAdmins: true + name: 'default' + recurringScansEmails: [ + 'test1@contoso.com' + 'test2@contoso.com' + ] + recurringScansIsEnabled: true + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +}] diff --git a/avm/res/sql/managed-instance/version.json b/avm/res/sql/managed-instance/version.json new file mode 100644 index 0000000000..7fa401bdf7 --- /dev/null +++ b/avm/res/sql/managed-instance/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} diff --git a/avm/res/sql/managed-instance/vulnerability-assessment/README.md b/avm/res/sql/managed-instance/vulnerability-assessment/README.md new file mode 100644 index 0000000000..62c9a59da2 --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/README.md @@ -0,0 +1,121 @@ +# SQL Managed Instance Vulnerability Assessments `[Microsoft.Sql/managedInstances/vulnerabilityAssessments]` + +This module deploys a SQL Managed Instance Vulnerability Assessment. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Sql/managedInstances/vulnerabilityAssessments` | [2023-08-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/managedInstances/vulnerabilityAssessments) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the vulnerability assessment. | +| [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | A blob storage to hold the scan results. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`managedInstanceName`](#parameter-managedinstancename) | string | The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`createStorageRoleAssignment`](#parameter-createstorageroleassignment) | bool | Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account. | +| [`recurringScansEmails`](#parameter-recurringscansemails) | array | Specifies an array of email addresses to which the scan notification is sent. | +| [`recurringScansEmailSubscriptionAdmins`](#parameter-recurringscansemailsubscriptionadmins) | bool | Specifies that the schedule scan notification will be is sent to the subscription administrators. | +| [`recurringScansIsEnabled`](#parameter-recurringscansisenabled) | bool | Recurring scans state. | +| [`useStorageAccountAccessKey`](#parameter-usestorageaccountaccesskey) | bool | Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account. | + +### Parameter: `name` + +The name of the vulnerability assessment. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountResourceId` + +A blob storage to hold the scan results. + +- Required: Yes +- Type: string + +### Parameter: `managedInstanceName` + +The name of the parent SQL managed instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `createStorageRoleAssignment` + +Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `recurringScansEmails` + +Specifies an array of email addresses to which the scan notification is sent. + +- Required: No +- Type: array +- Default: `[]` + +### Parameter: `recurringScansEmailSubscriptionAdmins` + +Specifies that the schedule scan notification will be is sent to the subscription administrators. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `recurringScansIsEnabled` + +Recurring scans state. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `useStorageAccountAccessKey` + +Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account. + +- Required: No +- Type: bool +- Default: `False` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed vulnerability assessment. | +| `resourceGroupName` | string | The resource group of the deployed vulnerability assessment. | +| `resourceId` | string | The resource ID of the deployed vulnerability assessment. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/managed-instance/vulnerability-assessment/main.bicep b/avm/res/sql/managed-instance/vulnerability-assessment/main.bicep new file mode 100644 index 0000000000..4bac1770f6 --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/main.bicep @@ -0,0 +1,64 @@ +metadata name = 'SQL Managed Instance Vulnerability Assessments' +metadata description = 'This module deploys a SQL Managed Instance Vulnerability Assessment.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the vulnerability assessment.') +param name string + +@description('Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment.') +param managedInstanceName string + +@description('Optional. Recurring scans state.') +param recurringScansIsEnabled bool = false + +@description('Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators.') +param recurringScansEmailSubscriptionAdmins bool = false + +@description('Optional. Specifies an array of email addresses to which the scan notification is sent.') +param recurringScansEmails array = [] + +@description('Required. A blob storage to hold the scan results.') +param storageAccountResourceId string + +@description('Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account.') +param useStorageAccountAccessKey bool = false + +@description('Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account.') +param createStorageRoleAssignment bool = true + +resource managedInstance 'Microsoft.Sql/managedInstances@2023-08-01-preview' existing = { + name: managedInstanceName +} + +// Assign SQL MI MSI access to storage account +module storageAccount_sbdc_rbac 'modules/nested_storageRoleAssignment.bicep' = if (!useStorageAccountAccessKey && createStorageRoleAssignment) { + name: '${managedInstance.name}-sbdc-rbac' + scope: resourceGroup(split(storageAccountResourceId, '/')[4]) + params: { + storageAccountName: last(split(storageAccountResourceId, '/')) + managedInstanceIdentityPrincipalId: managedInstance.identity.principalId + } +} + +resource vulnerabilityAssessment 'Microsoft.Sql/managedInstances/vulnerabilityAssessments@2023-08-01-preview' = { + name: name + parent: managedInstance + properties: { + storageContainerPath: 'https://${last(split(storageAccountResourceId, '/'))}.blob.${environment().suffixes.storage}/vulnerability-assessment/' + storageAccountAccessKey: useStorageAccountAccessKey ? listKeys(storageAccountResourceId, '2019-06-01').keys[0].value : any(null) + recurringScans: { + isEnabled: recurringScansIsEnabled + emailSubscriptionAdmins: recurringScansEmailSubscriptionAdmins + emails: recurringScansEmails + } + } +} + +@description('The name of the deployed vulnerability assessment.') +output name string = vulnerabilityAssessment.name + +@description('The resource ID of the deployed vulnerability assessment.') +output resourceId string = vulnerabilityAssessment.id + +@description('The resource group of the deployed vulnerability assessment.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/managed-instance/vulnerability-assessment/main.json b/avm/res/sql/managed-instance/vulnerability-assessment/main.json new file mode 100644 index 0000000000..8d0fef695c --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/main.json @@ -0,0 +1,167 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "15455405847587423486" + }, + "name": "SQL Managed Instance Vulnerability Assessments", + "description": "This module deploys a SQL Managed Instance Vulnerability Assessment.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the vulnerability assessment." + } + }, + "managedInstanceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL managed instance. Required if the template is used in a standalone deployment." + } + }, + "recurringScansIsEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Recurring scans state." + } + }, + "recurringScansEmailSubscriptionAdmins": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies that the schedule scan notification will be is sent to the subscription administrators." + } + }, + "recurringScansEmails": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies an array of email addresses to which the scan notification is sent." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. A blob storage to hold the scan results." + } + }, + "useStorageAccountAccessKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use Access Key to access the storage account. The storage account cannot be behind a firewall or virtual network. If an access key is not used, the SQL MI system assigned managed identity must be assigned the Storage Blob Data Contributor role on the storage account." + } + }, + "createStorageRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Create the Storage Blob Data Contributor role assignment on the storage account. Note, the role assignment must not already exist on the storage account." + } + } + }, + "resources": [ + { + "type": "Microsoft.Sql/managedInstances/vulnerabilityAssessments", + "apiVersion": "2023-08-01-preview", + "name": "[format('{0}/{1}', parameters('managedInstanceName'), parameters('name'))]", + "properties": { + "storageContainerPath": "[format('https://{0}.blob.{1}/vulnerability-assessment/', last(split(parameters('storageAccountResourceId'), '/')), environment().suffixes.storage)]", + "storageAccountAccessKey": "[if(parameters('useStorageAccountAccessKey'), listKeys(parameters('storageAccountResourceId'), '2019-06-01').keys[0].value, null())]", + "recurringScans": { + "isEnabled": "[parameters('recurringScansIsEnabled')]", + "emailSubscriptionAdmins": "[parameters('recurringScansEmailSubscriptionAdmins')]", + "emails": "[parameters('recurringScansEmails')]" + } + } + }, + { + "condition": "[and(not(parameters('useStorageAccountAccessKey')), parameters('createStorageRoleAssignment'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sbdc-rbac', parameters('managedInstanceName'))]", + "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "managedInstanceIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Sql/managedInstances', parameters('managedInstanceName')), '2023-08-01-preview', 'full').identity.principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18021215853157074333" + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. Storage account name." + } + }, + "managedInstanceIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "Required. Managed Identity Principal ID." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(format('{0}-{1}-Storage-Blob-Data-Contributor', resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), parameters('managedInstanceIdentityPrincipalId')))]", + "properties": { + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "principalId": "[parameters('managedInstanceIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + } + } + ] + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed vulnerability assessment." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed vulnerability assessment." + }, + "value": "[resourceId('Microsoft.Sql/managedInstances/vulnerabilityAssessments', parameters('managedInstanceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed vulnerability assessment." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep b/avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep new file mode 100644 index 0000000000..7ef3f28a53 --- /dev/null +++ b/avm/res/sql/managed-instance/vulnerability-assessment/modules/nested_storageRoleAssignment.bicep @@ -0,0 +1,20 @@ +@description('Required. Storage account name.') +param storageAccountName string + +@description('Required. Managed Identity Principal ID.') +param managedInstanceIdentityPrincipalId string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = { + name: storageAccountName +} + +// Assign Storage Blob Data Contributor RBAC role +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('${storageAccount.id}-${managedInstanceIdentityPrincipalId}-Storage-Blob-Data-Contributor') + scope: storageAccount + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalId: managedInstanceIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} From 950223f8de83d690fc272ed5cbac812b17e5ad43 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 19 Apr 2024 22:05:40 +0200 Subject: [PATCH 59/66] fix: Recovered missing role assignment workflow (#1719) ## Description Recovered missing role assignment workflow --- .../avm.ptn.authorization.role-assignment.yml | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/avm.ptn.authorization.role-assignment.yml diff --git a/.github/workflows/avm.ptn.authorization.role-assignment.yml b/.github/workflows/avm.ptn.authorization.role-assignment.yml new file mode 100644 index 0000000000..c9ad828152 --- /dev/null +++ b/.github/workflows/avm.ptn.authorization.role-assignment.yml @@ -0,0 +1,86 @@ +name: "avm.ptn.authorization.role-assignment" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.authorization.role-assignment.yml" + - "avm/ptn/authorization/role-assignment/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/authorization/role-assignment" + workflowPath: ".github/workflows/avm.ptn.authorization.role-assignment.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit From 7c25f74a8288ef15f8c1b158e48312e54e979af0 Mon Sep 17 00:00:00 2001 From: Richard Hooper Date: Fri, 19 Apr 2024 22:32:15 +0100 Subject: [PATCH 60/66] feat: added option to set backendpooltype. - `avm/res/container-service/managed-cluster` (#1709) ## Description This adds the option to change the backendpooltype. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=feat-rh-backendpooltype)](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Signed-off-by: PixelRobots <22979170+PixelRobots@users.noreply.github.com> --- .../managed-cluster/README.md | 16 ++++++++++++ .../managed-cluster/main.bicep | 20 ++++++++++----- .../managed-cluster/main.json | 25 +++++++++++++++---- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index 43e75d0fb7..f1f8091e4e 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -1501,6 +1501,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster: Date: Fri, 19 Apr 2024 23:48:50 +0100 Subject: [PATCH 61/66] docs: fixing issue #1064 - `avm/res/container-service/managed-cluster` (#1715) ## Description Closes #1064 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.container-service.managed-cluster](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=rh-container-service-docs-update)](https://github.com/PixelRobots/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings Signed-off-by: PixelRobots <22979170+PixelRobots@users.noreply.github.com> --- .../managed-cluster/README.md | 72 +++++++------------ .../managed-cluster/main.json | 2 +- .../tests/e2e/azure/main.test.bicep | 9 +-- .../tests/e2e/kubenet/main.test.bicep | 9 +-- .../tests/e2e/priv/main.test.bicep | 9 +-- .../tests/e2e/waf-aligned/main.test.bicep | 9 +-- 6 files changed, 37 insertions(+), 73 deletions(-) diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index f1f8091e4e..18b51cc75b 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -64,6 +64,9 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:' @@ -112,9 +112,6 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:", @@ -339,9 +336,6 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster: Date: Sun, 21 Apr 2024 22:15:19 +0200 Subject: [PATCH 62/66] feat: Added custom Azure storage replication rule and updated ps-rule (#1725) --- .../psrule/.ps-rule/custom-rules.Rule.yaml | 14 ++++++++++++++ .../pipelines/staticValidation/psrule/ps-rule.yaml | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml diff --git a/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml new file mode 100644 index 0000000000..c3bb28aae8 --- /dev/null +++ b/avm/utilities/pipelines/staticValidation/psrule/.ps-rule/custom-rules.Rule.yaml @@ -0,0 +1,14 @@ +# Synopsis: Use a zone or geo redundant storage account +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: 'Custom.Azure.Storage.UseReplication' +spec: + type: + - Microsoft.Storage/storageAccounts + condition: + anyOf: + - field: 'Sku.name' + contains: 'ZRS' + - field: 'Sku.name' + contains: 'GRS' diff --git a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml index f5bc660b72..b241ac0b1a 100644 --- a/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml +++ b/avm/utilities/pipelines/staticValidation/psrule/ps-rule.yaml @@ -74,8 +74,9 @@ configuration: rule: # Enable custom rules that don't exist in the baseline - includeLocal: false + includeLocal: true exclude: # Ignore the following rules for all resources - Azure.KeyVault.PurgeProtect - Azure.VM.UseHybridUseBenefit + - Azure.Storage.UseReplication From 557203abd1fe4993c82b0d4355aee1dbe09911c6 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 22 Apr 2024 23:47:05 +0200 Subject: [PATCH 63/66] fix: Update instance size options for API Management service - `avm/res/api-management/service` (#1629) Added '0' as an allowed value for instance size selection. Fixes #1628 ## Description ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [ ] Azure Verified Module updates: - [X] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/api-management/service/README.md | 1 + avm/res/api-management/service/main.bicep | 1 + avm/res/api-management/service/main.json | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/avm/res/api-management/service/README.md b/avm/res/api-management/service/README.md index 4b29ffb6c3..b8ca4b6ec8 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -1464,6 +1464,7 @@ The instance size of this API Management service. - Allowed: ```Bicep [ + 0 1 2 ] diff --git a/avm/res/api-management/service/main.bicep b/avm/res/api-management/service/main.bicep index 6224b65b85..429f44edce 100644 --- a/avm/res/api-management/service/main.bicep +++ b/avm/res/api-management/service/main.bicep @@ -66,6 +66,7 @@ param sku string = 'Developer' @description('Optional. The instance size of this API Management service.') @allowed([ + 0 1 2 ]) diff --git a/avm/res/api-management/service/main.json b/avm/res/api-management/service/main.json index 9f44ef9ee9..3a2f0a0a9c 100644 --- a/avm/res/api-management/service/main.json +++ b/avm/res/api-management/service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.54.24096", - "templateHash": "4652975451954294914" + "templateHash": "7048259745072727177" }, "name": "API Management Services", "description": "This module deploys an API Management Service.", @@ -381,6 +381,7 @@ "type": "int", "defaultValue": 1, "allowedValues": [ + 0, 1, 2 ], From 1dd8628c121e4bb2ba8fa6372bb3a202f2c6ad46 Mon Sep 17 00:00:00 2001 From: Buddy <38195643+tsc-buddy@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:20:19 +1200 Subject: [PATCH 64/66] fix: AppServicePlan - SKU selection improvements (#1736) ## Description Provides improvements to simplify the selection of SKUs for App Service Plans by relying on the RP to provide the values for 'tier', 'family' and 'size' properties based on the 'name' provided. Logic adjusted on ZR param to accommodate the change also. Closes #1506 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.serverfarm](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=fix%2F1506-sku-updates)](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/web/serverfarm/README.md | 84 ++++++++----------- avm/res/web/serverfarm/main.bicep | 25 +++--- avm/res/web/serverfarm/main.json | 23 +++-- .../tests/e2e/defaults/main.test.bicep | 9 +- .../serverfarm/tests/e2e/max/main.test.bicep | 9 +- .../tests/e2e/waf-aligned/main.test.bicep | 9 +- avm/res/web/serverfarm/version.json | 2 +- 7 files changed, 71 insertions(+), 90 deletions(-) diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 1a3e585904..74b625e14a 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -47,13 +47,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfmin001' - sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' - } + skuCapacity: 2 + skuName: 'S1' // Non-required parameters location: '' } @@ -76,14 +71,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmin001" }, - "sku": { - "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" - } + "skuCapacity": { + "value": 2 + }, + "skuName": { + "value": "S1" }, // Non-required parameters "location": { @@ -111,13 +103,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfmax001' - sku: { - capacity: 1 - family: 'S' - name: 'S1' - size: 'S1' - tier: 'Standard' - } + skuCapacity: 1 + skuName: 'S1' // Non-required parameters diagnosticSettings: [ { @@ -183,14 +170,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmax001" }, - "sku": { - "value": { - "capacity": 1, - "family": "S", - "name": "S1", - "size": "S1", - "tier": "Standard" - } + "skuCapacity": { + "value": 1 + }, + "skuName": { + "value": "S1" }, // Non-required parameters "diagnosticSettings": { @@ -275,13 +259,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfwaf001' - sku: { - capacity: 3 - family: 'P' - name: 'P1v3' - size: 'P1v3' - tier: 'Premium' - } + skuCapacity: 3 + skuName: 'P1v3' // Non-required parameters diagnosticSettings: [ { @@ -329,14 +308,11 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfwaf001" }, - "sku": { - "value": { - "capacity": 3, - "family": "P", - "name": "P1v3", - "size": "P1v3", - "tier": "Premium" - } + "skuCapacity": { + "value": 3 + }, + "skuName": { + "value": "P1v3" }, // Non-required parameters "diagnosticSettings": { @@ -392,7 +368,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the app service plan. | -| [`sku`](#parameter-sku) | object | Defines the name, tier, size, family and capacity of the App Service Plan. | +| [`skuCapacity`](#parameter-skucapacity) | int | Number of workers associated with the App Service Plan. | +| [`skuName`](#parameter-skuname) | string | The name of the SKU will Determine the tier, size, family of the App Service Plan. | **Conditional parameters** @@ -426,12 +403,19 @@ Name of the app service plan. - Required: Yes - Type: string -### Parameter: `sku` +### Parameter: `skuCapacity` -Defines the name, tier, size, family and capacity of the App Service Plan. +Number of workers associated with the App Service Plan. - Required: Yes -- Type: object +- Type: int + +### Parameter: `skuName` + +The name of the SKU will Determine the tier, size, family of the App Service Plan. + +- Required: Yes +- Type: string ### Parameter: `reserved` @@ -778,7 +762,7 @@ Zone Redundancy can only be used on Premium or ElasticPremium SKU Tiers within Z - Required: No - Type: bool -- Default: `[if(or(equals(parameters('sku').tier, 'Premium'), equals(parameters('sku').tier, 'ElasticPremium')), true(), false())]` +- Default: `[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]` ## Outputs diff --git a/avm/res/web/serverfarm/main.bicep b/avm/res/web/serverfarm/main.bicep index 5deb3fe099..5eb7e567b6 100644 --- a/avm/res/web/serverfarm/main.bicep +++ b/avm/res/web/serverfarm/main.bicep @@ -7,19 +7,19 @@ metadata owner = 'Azure/module-maintainers' @maxLength(60) param name string -@description('Required. Defines the name, tier, size, family and capacity of the App Service Plan.') +@description('Required. The name of the SKU will Determine the tier, size, family of the App Service Plan.') @metadata({ example: ''' - { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + 'F1' + 'B1' + 'P1v3' + 'I1v2' ''' }) -param sku object +param skuName string + +@description('Required. Number of workers associated with the App Service Plan.') +param skuCapacity int @description('Optional. Location for all resources.') param location string = resourceGroup().location @@ -61,7 +61,7 @@ param targetWorkerCount int = 0 param targetWorkerSize int = 0 @description('Optional. Zone Redundancy can only be used on Premium or ElasticPremium SKU Tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs).') -param zoneRedundant bool = (sku.tier == 'Premium' || sku.tier == 'ElasticPremium') ? true : false +param zoneRedundant bool = startsWith(skuName, 'P') || startsWith(skuName, 'EP') ? true : false @description('Optional. The lock settings of the service.') param lock lockType @@ -124,7 +124,10 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2022-09-01' = { kind: kind location: location tags: tags - sku: sku + sku: { + name: skuName + capacity: skuCapacity + } properties: { workerTierName: workerTierName hostingEnvironmentProfile: !empty(appServiceEnvironmentId) diff --git a/avm/res/web/serverfarm/main.json b/avm/res/web/serverfarm/main.json index 68999ed0f2..1e9678c755 100644 --- a/avm/res/web/serverfarm/main.json +++ b/avm/res/web/serverfarm/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.26.170.59819", - "templateHash": "16669238654401736455" + "templateHash": "5846778493081554072" }, "name": "App Service Plan", "description": "This module deploys an App Service Plan.", @@ -201,11 +201,17 @@ "description": "Required. Name of the app service plan." } }, - "sku": { - "type": "object", + "skuName": { + "type": "string", "metadata": { - "example": " {\n name: 'P1v3'\n tier: 'Premium'\n size: 'P1v3'\n family: 'P'\n capacity: 3\n }\n ", - "description": "Required. Defines the name, tier, size, family and capacity of the App Service Plan." + "example": " 'F1'\n 'B1'\n 'P1v3'\n 'I1v2'\n ", + "description": "Required. The name of the SKU will Determine the tier, size, family of the App Service Plan." + } + }, + "skuCapacity": { + "type": "int", + "metadata": { + "description": "Required. Number of workers associated with the App Service Plan." } }, "location": { @@ -285,7 +291,7 @@ }, "zoneRedundant": { "type": "bool", - "defaultValue": "[if(or(equals(parameters('sku').tier, 'Premium'), equals(parameters('sku').tier, 'ElasticPremium')), true(), false())]", + "defaultValue": "[if(or(startsWith(parameters('skuName'), 'P'), startsWith(parameters('skuName'), 'EP')), true(), false())]", "metadata": { "description": "Optional. Zone Redundancy can only be used on Premium or ElasticPremium SKU Tiers within ZRS Supported regions (https://learn.microsoft.com/en-us/azure/storage/common/redundancy-regions-zrs)." } @@ -362,7 +368,10 @@ "kind": "[parameters('kind')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", - "sku": "[parameters('sku')]", + "sku": { + "name": "[parameters('skuName')]", + "capacity": "[parameters('skuCapacity')]" + }, "properties": { "workerTierName": "[parameters('workerTierName')]", "hostingEnvironmentProfile": "[if(not(empty(parameters('appServiceEnvironmentId'))), createObject('id', parameters('appServiceEnvironmentId')), null())]", diff --git a/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep index 9f2ffe8e94..0183742502 100644 --- a/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep @@ -46,13 +46,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + skuName: 'S1' + skuCapacity: 2 } } ] diff --git a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep index f7e0fb3a88..b987bfb1ae 100644 --- a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep @@ -67,13 +67,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'S1' - tier: 'Standard' - size: 'S1' - family: 'S' - capacity: 1 - } + skuName: 'S1' + skuCapacity: 1 perSiteScaling: true zoneRedundant: false kind: 'App' diff --git a/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep index 5cd23e57c9..45080f9d3b 100644 --- a/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep @@ -58,13 +58,8 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: tempLocation - sku: { - name: 'P1v3' - tier: 'Premium' - size: 'P1v3' - family: 'P' - capacity: 3 - } + skuName: 'P1v3' + skuCapacity: 3 zoneRedundant: true kind: 'App' lock: { diff --git a/avm/res/web/serverfarm/version.json b/avm/res/web/serverfarm/version.json index 8def869ede..daf1a794d9 100644 --- a/avm/res/web/serverfarm/version.json +++ b/avm/res/web/serverfarm/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] From 9fc50746ca6981becc7d2e6df9a611c835da685f Mon Sep 17 00:00:00 2001 From: Yas Date: Tue, 23 Apr 2024 12:28:57 +1000 Subject: [PATCH 65/66] feat: `avm/res/sql/instance-pool` (#1714) ## Description This PR adds the new Azure SQL Instance pool and its tests Closes [Module Proposal : avm/res/sql/instance-pool](#752) ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.sql.instance-pool](https://github.com/yashints/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml/badge.svg)](https://github.com/yashints/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml) | ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../workflows/avm.res.sql.instance-pool.yml | 86 ++++++ avm/res/sql/instance-pool/README.md | 286 ++++++++++++++++++ avm/res/sql/instance-pool/main.bicep | 102 +++++++ avm/res/sql/instance-pool/main.json | 176 +++++++++++ .../tests/e2e/defaults/dependencies.bicep | 286 ++++++++++++++++++ .../tests/e2e/defaults/main.test.bicep | 61 ++++ .../tests/e2e/waf-aligned/dependencies.bicep | 286 ++++++++++++++++++ .../tests/e2e/waf-aligned/main.test.bicep | 62 ++++ avm/res/sql/instance-pool/version.json | 7 + 11 files changed, 1354 insertions(+) create mode 100644 .github/workflows/avm.res.sql.instance-pool.yml create mode 100644 avm/res/sql/instance-pool/README.md create mode 100644 avm/res/sql/instance-pool/main.bicep create mode 100644 avm/res/sql/instance-pool/main.json create mode 100644 avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/sql/instance-pool/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0734b0fb91..68a24d86e1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -131,6 +131,7 @@ /avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/signal-r/ @Azure/avm-res-signalrservice-signalr-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/signal-r-service/web-pub-sub/ @Azure/avm-res-signalrservice-webpubsub-module-owners-bicep @Azure/avm-core-team-technical-bicep +/avm/res/sql/instance-pool/ @Azure/avm-res-sql-instancepool-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/sql/managed-instance/ @Azure/avm-res-sql-managedinstance-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/sql/server/ @Azure/avm-res-sql-server-module-owners-bicep @Azure/avm-core-team-technical-bicep /avm/res/storage/storage-account/ @Azure/avm-res-storage-storageaccount-module-owners-bicep @Azure/avm-core-team-technical-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 8ad0c07acd..9a7da432aa 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -164,6 +164,7 @@ body: - "avm/res/service-fabric/cluster" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" + - "avm/res/sql/instance-pool" - "avm/res/sql/managed-instance" - "avm/res/sql/server" - "avm/res/storage/storage-account" diff --git a/.github/workflows/avm.res.sql.instance-pool.yml b/.github/workflows/avm.res.sql.instance-pool.yml new file mode 100644 index 0000000000..75f6103864 --- /dev/null +++ b/.github/workflows/avm.res.sql.instance-pool.yml @@ -0,0 +1,86 @@ +name: "avm.res.sql.instance-pool" + +on: + schedule: + - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month) + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.sql.instance-pool.yml" + - "avm/res/sql/instance-pool/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/sql/instance-pool" + workflowPath: ".github/workflows/avm.res.sql.instance-pool.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/sql/instance-pool/README.md b/avm/res/sql/instance-pool/README.md new file mode 100644 index 0000000000..68e73a8f95 --- /dev/null +++ b/avm/res/sql/instance-pool/README.md @@ -0,0 +1,286 @@ +# SQL Server Instance Pool `[Microsoft.Sql/instancePools]` + +This module deploys an Azure SQL Server Instance Pool. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Sql/instancePools` | [2023-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Sql/2023-05-01-preview/instancePools) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/sql/instance-pool:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [WAF-aligned](#example-2-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module instancePool 'br/public:avm/res/sql/instance-pool:' = { + name: 'instancePoolDeployment' + params: { + // Required parameters + name: '' + subnetResourceId: '' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _WAF-aligned_ + +This instance deploys the module in alignment with the best-practices of the Well-Architected Framework. + + +

+ +via Bicep module + +```bicep +module instancePool 'br/public:avm/res/sql/instance-pool:' = { + name: 'instancePoolDeployment' + params: { + // Required parameters + name: '' + subnetResourceId: '' + // Non-required parameters + location: '' + skuName: 'GP_Gen8IM' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + }, + "skuName": { + "value": "GP_Gen8IM" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | The name of the instance pool. | +| [`subnetResourceId`](#parameter-subnetresourceid) | string | The subnet resource ID for the instance pool. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`licenseType`](#parameter-licensetype) | string | The license type to apply for this database. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`skuFamily`](#parameter-skufamily) | string | If the service has different generations of hardware, for the same SKU, then that can be captured here. | +| [`skuName`](#parameter-skuname) | string | The SKU name for the instance pool. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | +| [`tier`](#parameter-tier) | string | The vCore service tier for the instance pool. | +| [`vCores`](#parameter-vcores) | int | The number of vCores for the instance pool. | + +### Parameter: `name` + +The name of the instance pool. + +- Required: Yes +- Type: string + +### Parameter: `subnetResourceId` + +The subnet resource ID for the instance pool. + +- Required: Yes +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `licenseType` + +The license type to apply for this database. + +- Required: No +- Type: string +- Default: `'BasePrice'` +- Allowed: + ```Bicep + [ + 'BasePrice' + 'LicenseIncluded' + ] + ``` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `skuFamily` + +If the service has different generations of hardware, for the same SKU, then that can be captured here. + +- Required: No +- Type: string +- Default: `'Gen5'` + +### Parameter: `skuName` + +The SKU name for the instance pool. + +- Required: No +- Type: string +- Default: `'GP_Gen5'` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `tier` + +The vCore service tier for the instance pool. + +- Required: No +- Type: string +- Default: `'GeneralPurpose'` +- Allowed: + ```Bicep + [ + 'GeneralPurpose' + ] + ``` + +### Parameter: `vCores` + +The number of vCores for the instance pool. + +- Required: No +- Type: int +- Default: `8` +- Allowed: + ```Bicep + [ + 8 + 16 + 24 + 32 + 40 + 64 + 80 + 96 + 128 + 160 + 192 + 224 + 256 + ] + ``` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `instancePoolLocation` | string | The location of the SQL instance pool. | +| `name` | string | The name of the SQL instance pool. | +| `resourceGroupName` | string | The resource group name of the SQL instance pool. | +| `resourceId` | string | The ID of the SQL instance pool. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/sql/instance-pool/main.bicep b/avm/res/sql/instance-pool/main.bicep new file mode 100644 index 0000000000..088cd87ada --- /dev/null +++ b/avm/res/sql/instance-pool/main.bicep @@ -0,0 +1,102 @@ +metadata name = 'SQL Server Instance Pool' +metadata description = 'This module deploys an Azure SQL Server Instance Pool.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the instance pool.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Required. The subnet resource ID for the instance pool.') +param subnetResourceId string + +@description('Optional. The license type to apply for this database.') +@allowed([ + 'BasePrice' + 'LicenseIncluded' +]) +param licenseType string = 'BasePrice' + +@description('Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here.') +param skuFamily string = 'Gen5' + +@description('Optional. The number of vCores for the instance pool.') +@allowed([ + 8 + 16 + 24 + 32 + 40 + 64 + 80 + 96 + 128 + 160 + 192 + 224 + 256 +]) +param vCores int = 8 + +@description('Optional. The vCore service tier for the instance pool.') +@allowed([ + 'GeneralPurpose' +]) +param tier string = 'GeneralPurpose' + +@description('Optional. The SKU name for the instance pool.') +param skuName string = 'GP_Gen5' + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +resource instancePool 'Microsoft.Sql/instancePools@2023-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + family: skuFamily + name: skuName + tier: tier + } + properties: { + licenseType: licenseType + subnetId: subnetResourceId + vCores: vCores + } +} + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = + if (enableTelemetry) { + name: '46d3xbcp.res.sql-instancepool.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } + } + +@description('The ID of the SQL instance pool.') +output resourceId string = instancePool.id + +@description('The name of the SQL instance pool.') +output name string = instancePool.name + +@description('The location of the SQL instance pool.') +output instancePoolLocation string = instancePool.location + +@description('The resource group name of the SQL instance pool.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/sql/instance-pool/main.json b/avm/res/sql/instance-pool/main.json new file mode 100644 index 0000000000..684f086ed4 --- /dev/null +++ b/avm/res/sql/instance-pool/main.json @@ -0,0 +1,176 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "1210707271653756300" + }, + "name": "SQL Server Instance Pool", + "description": "This module deploys an Azure SQL Server Instance Pool.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the instance pool." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The subnet resource ID for the instance pool." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "BasePrice", + "allowedValues": [ + "BasePrice", + "LicenseIncluded" + ], + "metadata": { + "description": "Optional. The license type to apply for this database." + } + }, + "skuFamily": { + "type": "string", + "defaultValue": "Gen5", + "metadata": { + "description": "Optional. If the service has different generations of hardware, for the same SKU, then that can be captured here." + } + }, + "vCores": { + "type": "int", + "defaultValue": 8, + "allowedValues": [ + 8, + 16, + 24, + 32, + 40, + 64, + 80, + 96, + 128, + 160, + 192, + 224, + 256 + ], + "metadata": { + "description": "Optional. The number of vCores for the instance pool." + } + }, + "tier": { + "type": "string", + "defaultValue": "GeneralPurpose", + "allowedValues": [ + "GeneralPurpose" + ], + "metadata": { + "description": "Optional. The vCore service tier for the instance pool." + } + }, + "skuName": { + "type": "string", + "defaultValue": "GP_Gen5", + "metadata": { + "description": "Optional. The SKU name for the instance pool." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "instancePool": { + "type": "Microsoft.Sql/instancePools", + "apiVersion": "2023-05-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "family": "[parameters('skuFamily')]", + "name": "[parameters('skuName')]", + "tier": "[parameters('tier')]" + }, + "properties": { + "licenseType": "[parameters('licenseType')]", + "subnetId": "[parameters('subnetResourceId')]", + "vCores": "[parameters('vCores')]" + } + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.sql-instancepool.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The ID of the SQL instance pool." + }, + "value": "[resourceId('Microsoft.Sql/instancePools', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL instance pool." + }, + "value": "[parameters('name')]" + }, + "instancePoolLocation": { + "type": "string", + "metadata": { + "description": "The location of the SQL instance pool." + }, + "value": "[reference('instancePool', '2023-05-01-preview', 'full').location]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group name of the SQL instance pool." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep b/avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..772bd070fc --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,286 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Deployment Script to create to get the paired region name.') +param pairedRegionScriptName string + +@description('Required. The name of the SQL Instance Pool.') +param sqlInstancePoolName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The name of the Subnet to create.') +param subnetName string = 'sql-instancepool-subnet' + +@description('Required. The name of the NSG to create.') +param nsgName string + +@description('Required. The name of the route table to create.') +param routeTableName string + +var addressPrefix = '10.0.0.0/16' +var subnetAddressPrefix = cidrSubnet(addressPrefix, 24, 0) + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${location}-${managedIdentity.id}-Reader-RoleAssignment') + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) // Reader + principalType: 'ServicePrincipal' + } +} + +resource getPairedRegionScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: pairedRegionScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-Location \\"${location}\\"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Get-PairedRegion.ps1') + } + dependsOn: [ + roleAssignment + ] +} + +resource nsg 'Microsoft.Network/networkSecurityGroups@2020-06-01' = { + name: nsgName + location: location + properties: { + securityRules: [ + { + name: 'sqlmi-healthprobe-in' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + { + name: 'sqlmi-internal-in' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + } + } + { + name: 'sqlmi-aad-out' + properties: { + description: 'Allow communication with Azure Active Directory over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureActiveDirectory' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'sqlmi-onedsc-out' + properties: { + description: 'Allow communication with the One DS Collector over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'OneDsCollector' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'sqlmi-internal-out' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-p-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${location}' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-s-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + access: 'Allow' + priority: 105 + direction: 'Outbound' + } + } + { + name: 'sqlmi-optional-azure-out' + properties: { + description: 'Allow AzureCloud outbound https traffic' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2020-06-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'sqlmi_subnet-to-vnetlocal' + properties: { + addressPrefix: subnetAddressPrefix + nextHopType: 'VnetLocal' + } + } + { + name: 'sqlmi-aad' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-odscollector' + properties: { + addressPrefix: 'OneDsCollector' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-p' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-s' + properties: { + addressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azure-p' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azurecloud-s' + properties: { + addressPrefix: 'AzureCloud.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + networkSecurityGroup: { + id: nsg.id + } + routeTable: { + id: routeTable.id + } + delegations: [ + { + name: 'ip-delegations-${sqlInstancePoolName}' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + } + ] + } +} + +@description('The subnetId required for the instance pool creation.') +output subnetId string = virtualNetwork.properties.subnets[0].id + +@description('The name of the sql instnce pool to be created.') +output sqlInstancePoolName string = sqlInstancePoolName diff --git a/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep b/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..677a80dbe1 --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.instancepool-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test +param serviceShort string = 'sipmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + nsgName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + sqlInstancePoolName: '${namePrefix}${serviceShort}001' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: nestedDependencies.outputs.sqlInstancePoolName + location: resourceLocation + subnetResourceId: nestedDependencies.outputs.subnetId + } +} diff --git a/avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..772bd070fc --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,286 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Deployment Script to create to get the paired region name.') +param pairedRegionScriptName string + +@description('Required. The name of the SQL Instance Pool.') +param sqlInstancePoolName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The name of the Subnet to create.') +param subnetName string = 'sql-instancepool-subnet' + +@description('Required. The name of the NSG to create.') +param nsgName string + +@description('Required. The name of the route table to create.') +param routeTableName string + +var addressPrefix = '10.0.0.0/16' +var subnetAddressPrefix = cidrSubnet(addressPrefix, 24, 0) + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${location}-${managedIdentity.id}-Reader-RoleAssignment') + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) // Reader + principalType: 'ServicePrincipal' + } +} + +resource getPairedRegionScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: pairedRegionScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-Location \\"${location}\\"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Get-PairedRegion.ps1') + } + dependsOn: [ + roleAssignment + ] +} + +resource nsg 'Microsoft.Network/networkSecurityGroups@2020-06-01' = { + name: nsgName + location: location + properties: { + securityRules: [ + { + name: 'sqlmi-healthprobe-in' + properties: { + description: 'Allow Azure Load Balancer inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 100 + direction: 'Inbound' + } + } + { + name: 'sqlmi-internal-in' + properties: { + description: 'Allow MI internal inbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 101 + direction: 'Inbound' + } + } + { + name: 'sqlmi-aad-out' + properties: { + description: 'Allow communication with Azure Active Directory over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureActiveDirectory' + access: 'Allow' + priority: 101 + direction: 'Outbound' + } + } + { + name: 'sqlmi-onedsc-out' + properties: { + description: 'Allow communication with the One DS Collector over https' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'OneDsCollector' + access: 'Allow' + priority: 102 + direction: 'Outbound' + } + } + { + name: 'sqlmi-internal-out' + properties: { + description: 'Allow MI internal outbound traffic' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: subnetAddressPrefix + access: 'Allow' + priority: 103 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-p-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${location}' + access: 'Allow' + priority: 104 + direction: 'Outbound' + } + } + { + name: 'sqlmi-strg-s-out' + properties: { + description: 'Allow outbound communication with storage over HTTPS' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + access: 'Allow' + priority: 105 + direction: 'Outbound' + } + } + { + name: 'sqlmi-optional-azure-out' + properties: { + description: 'Allow AzureCloud outbound https traffic' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: subnetAddressPrefix + destinationAddressPrefix: 'AzureCloud' + access: 'Allow' + priority: 100 + direction: 'Outbound' + } + } + ] + } +} + +resource routeTable 'Microsoft.Network/routeTables@2020-06-01' = { + name: routeTableName + location: location + properties: { + disableBgpRoutePropagation: false + routes: [ + { + name: 'sqlmi_subnet-to-vnetlocal' + properties: { + addressPrefix: subnetAddressPrefix + nextHopType: 'VnetLocal' + } + } + { + name: 'sqlmi-aad' + properties: { + addressPrefix: 'AzureActiveDirectory' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-odscollector' + properties: { + addressPrefix: 'OneDsCollector' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-p' + properties: { + addressPrefix: 'Storage.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-stg-s' + properties: { + addressPrefix: 'Storage.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azure-p' + properties: { + addressPrefix: 'AzureCloud.${location}' + nextHopType: 'Internet' + } + } + { + name: 'sqlmi-optional-azurecloud-s' + properties: { + addressPrefix: 'AzureCloud.${getPairedRegionScript.properties.outputs.pairedRegionName}' + nextHopType: 'Internet' + } + } + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + networkSecurityGroup: { + id: nsg.id + } + routeTable: { + id: routeTable.id + } + delegations: [ + { + name: 'ip-delegations-${sqlInstancePoolName}' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + } + ] + } +} + +@description('The subnetId required for the instance pool creation.') +output subnetId string = virtualNetwork.properties.subnets[0].id + +@description('The name of the sql instnce pool to be created.') +output sqlInstancePoolName string = sqlInstancePoolName diff --git a/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..ffaa207271 --- /dev/null +++ b/avm/res/sql/instance-pool/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-sql.instancepool-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test +param serviceShort string = 'sipwaf' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + nsgName: 'dep-${namePrefix}-nsg-${serviceShort}' + routeTableName: 'dep-${namePrefix}-rt-${serviceShort}' + sqlInstancePoolName: '${namePrefix}${serviceShort}001' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + name: nestedDependencies.outputs.sqlInstancePoolName + location: resourceLocation + skuName: 'GP_Gen8IM' + subnetResourceId: nestedDependencies.outputs.subnetId + } +} diff --git a/avm/res/sql/instance-pool/version.json b/avm/res/sql/instance-pool/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/sql/instance-pool/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 31898eddd28967a6560f1d98d97f5c144935b652 Mon Sep 17 00:00:00 2001 From: Christoph Vollmann Date: Tue, 23 Apr 2024 04:58:27 +0200 Subject: [PATCH 66/66] fix: 1704 Web Site without Managed Identity (#1738) ## Description This PR changes the default value for Managed Identity Type from `null` to `'None'` to allow creation of sites without Managed Identity as per documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.web/sites?pivots=deployment-language-bicep#managedserviceidentity. The pipeline run shows Bicep Linter warnings for the `identity` property in the pester tests: https://github.com/cloudchristoph/bicep-registry-modules/actions/runs/8783746073/job/24100560440#step:4:234 ``` /home/runner/work/bicep-registry-modules/bicep-registry-modules/avm/res/web/site/main.bicep(227,13) : Warning BCP036: The property "type" expected a value of type "'None' | 'SystemAssigned' | 'SystemAssigned, UserAssigned' | 'UserAssigned' | null" but the provided value in source declaration "identity" is of type "'None' | 'SystemAssigned' | 'SystemAssigned,UserAssigned' | 'UserAssigned'". If this is an inaccuracy in the documentation, please report it to the Bicep Team. [https://aka.ms/bicep-type-issues] ``` The warning also has existed before, but for the missing `'None'` value, so I think it's not an issue. Regarding the backwards compatibility: This change doesn't affect the actual deployment. Managed Identities are disabled no matter which of the two values is given. Fixes #1704 Closes #1704 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.site](https://github.com/cloudchristoph/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=1704_site_without_managed_identity)](https://github.com/cloudchristoph/bicep-registry-modules/actions/workflows/avm.res.web.site.yml)| ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings (see above) --- avm/res/web/site/main.bicep | 2 +- avm/res/web/site/main.json | 42 ++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index 19fa05ed2d..9c2aefa0e4 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -169,7 +169,7 @@ var identity = !empty(managedIdentities) ? { type: (managedIdentities.?systemAssigned ?? false) ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') - : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : null) + : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None') userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null } : null diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index 3b31a486aa..607d3adbb5 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "14301920664802681394" + "version": "0.26.170.59819", + "templateHash": "5830272725104890600" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -747,7 +747,7 @@ }, "variables": { "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { "App Compliance Automation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f37683f-2463-46b6-9ce7-9b788b988ba2')]", "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -929,8 +929,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "18064037455551234601" + "version": "0.26.170.59819", + "templateHash": "12051629915105082529" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting.", @@ -1080,8 +1080,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "9660352953607316036" + "version": "0.26.170.59819", + "templateHash": "14303407385986258247" }, "name": "Site Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -1300,8 +1300,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "7436278786647572492" + "version": "0.26.170.59819", + "templateHash": "5665258089530156840" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -2208,8 +2208,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12764984503745572183" + "version": "0.26.170.59819", + "templateHash": "16550727222833899283" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -2378,8 +2378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1333776366661137381" + "version": "0.26.170.59819", + "templateHash": "512112730780239094" }, "name": "Site Slot Auth Settings V2 Config", "description": "This module deploys a Site Auth Settings V2 Configuration.", @@ -2494,8 +2494,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "2236066853450471067" + "version": "0.26.170.59819", + "templateHash": "4923254671011353973" }, "name": "Web Site Slot Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Slot Basic Publishing Credentials Policy.", @@ -2620,8 +2620,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "1058820827217282473" + "version": "0.26.170.59819", + "templateHash": "15033433727480709023" }, "name": "Web/Function Apps Slot Hybrid Connection Relay", "description": "This module deploys a Site Slot Hybrid Connection Namespace Relay.", @@ -3440,8 +3440,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12379291046700283915" + "version": "0.26.170.59819", + "templateHash": "1590652329081458395" }, "name": "Web Site Basic Publishing Credentials Policies", "description": "This module deploys a Web Site Basic Publishing Credentials Policy.", @@ -3556,8 +3556,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.54.24096", - "templateHash": "12607693486765150465" + "version": "0.26.170.59819", + "templateHash": "15287918657229788223" }, "name": "Web/Function Apps Hybrid Connection Relay", "description": "This module deploys a Site Hybrid Connection Namespace Relay.",