From d785b8291a3ff094f73cd4b8b1f62db0681894c5 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Thu, 25 Jan 2024 14:59:52 -0500 Subject: [PATCH 01/12] initial AVD add --- docs/content/patterns/avd/FAQ.md | 33 + docs/content/patterns/avd/Known-Issues.md | 8 + docs/content/patterns/avd/Telemetry.md | 64 + docs/content/patterns/avd/Whats-New.md | 20 + docs/content/patterns/avd/_index.md | 101 + ...troduction-to-deploying-the-AVD-Pattern.md | 37 + docs/content/patterns/avd/deploy/_index.md | 6 + docs/content/patterns/avd/media/alerts.xlsx | Bin 0 -> 15463 bytes .../patterns/avd/media/avdAlertRules.jpg | Bin 0 -> 142913 bytes .../avd/media/avdAlertRulesChange.jpg | Bin 0 -> 170978 bytes .../avd/media/avdAlertRulesChangeLogbased.jpg | Bin 0 -> 333532 bytes .../media/avdAlertRulesChangeMetricbased.jpg | Bin 0 -> 184866 bytes .../patterns/avd/media/avdAlertRulesCopy1.jpg | Bin 0 -> 28183 bytes .../patterns/avd/media/avdAlertRulesCopy2.jpg | Bin 0 -> 144235 bytes .../patterns/avd/media/avdAlertRulesCopy3.jpg | Bin 0 -> 102466 bytes .../avd/media/avdAlertRulesEnable.jpg | Bin 0 -> 160350 bytes .../avd/media/avdAlertRulesFilter.jpg | Bin 0 -> 134858 bytes .../avd/media/avdAlertRulesProperties.jpg | Bin 0 -> 69863 bytes .../avd/media/avdAlertRulesProperties2.jpg | Bin 0 -> 329400 bytes patterns/avd/avdArm.json | 13913 ++++++++++++++++ patterns/avd/avdCustomUi.json | 472 + patterns/avd/examples/sample-pipeline.yml | 17 + patterns/avd/examples/sample-workflow.yml | 37 + patterns/avd/scripts/Get-HostPoolInfo.ps1 | 68 + patterns/avd/scripts/Get-StorAcctInfo.ps1 | 69 + .../locks/.test/common/deploy.test.bicep | 43 + .../locks/deploy.bicep | 71 + .../Microsoft.Authorization/locks/readme.md | 108 + .../locks/resourceGroup/deploy.bicep | 50 + .../locks/resourceGroup/readme.md | 46 + .../locks/resourceGroup/version.json | 4 + .../locks/subscription/deploy.bicep | 50 + .../locks/subscription/readme.md | 46 + .../locks/subscription/version.json | 4 + .../locks/version.json | 4 + .../.test/mg.common/deploy.test.bicep | 90 + .../.test/mg.min/deploy.test.bicep | 24 + .../.test/rg.common/dependencies.bicep | 33 + .../.test/rg.common/deploy.test.bicep | 117 + .../.test/rg.min/deploy.test.bicep | 44 + .../.test/sub.common/dependencies.bicep | 13 + .../.test/sub.common/deploy.test.bicep | 114 + .../.test/sub.min/deploy.test.bicep | 25 + .../policyAssignments/deploy.bicep | 167 + .../managementGroup/deploy.bicep | 124 + .../managementGroup/readme.md | 60 + .../managementGroup/version.json | 4 + .../policyAssignments/readme.md | 953 ++ .../resourceGroup/deploy.bicep | 129 + .../policyAssignments/resourceGroup/readme.md | 62 + .../resourceGroup/version.json | 4 + .../subscription/deploy.bicep | 124 + .../policyAssignments/subscription/readme.md | 60 + .../subscription/version.json | 4 + .../policyAssignments/version.json | 4 + .../.test/mg.common/deploy.test.bicep | 73 + .../.test/mg.min/deploy.test.bicep | 45 + .../.test/sub.common/deploy.test.bicep | 73 + .../.test/sub.min/deploy.test.bicep | 45 + .../policyDefinitions/deploy.bicep | 100 + .../managementGroup/deploy.bicep | 73 + .../managementGroup/readme.md | 50 + .../managementGroup/version.json | 4 + .../policyDefinitions/readme.md | 635 + .../subscription/deploy.bicep | 73 + .../policyDefinitions/subscription/readme.md | 50 + .../subscription/version.json | 4 + .../policyDefinitions/version.json | 4 + .../.test/mg.common/deploy.test.bicep | 112 + .../.test/mg.min/deploy.test.bicep | 42 + .../.test/rg.common/deploy.test.bicep | 121 + .../.test/rg.min/deploy.test.bicep | 52 + .../.test/sub.common/deploy.test.bicep | 111 + .../.test/sub.min/deploy.test.bicep | 42 + .../policyExemptions/deploy.bicep | 133 + .../managementGroup/deploy.bicep | 85 + .../managementGroup/readme.md | 53 + .../managementGroup/version.json | 4 + .../policyExemptions/readme.md | 700 + .../resourceGroup/deploy.bicep | 84 + .../policyExemptions/resourceGroup/readme.md | 53 + .../resourceGroup/version.json | 4 + .../subscription/deploy.bicep | 85 + .../policyExemptions/subscription/readme.md | 53 + .../subscription/version.json | 4 + .../policyExemptions/version.json | 4 + .../.test/mg.common/deploy.test.bicep | 68 + .../.test/mg.min/deploy.test.bicep | 35 + .../.test/sub.common/deploy.test.bicep | 68 + .../.test/sub.min/deploy.test.bicep | 35 + .../policySetDefinitions/deploy.bicep | 89 + .../managementGroup/deploy.bicep | 62 + .../managementGroup/readme.md | 49 + .../managementGroup/version.json | 4 + .../policySetDefinitions/readme.md | 573 + .../subscription/deploy.bicep | 62 + .../subscription/readme.md | 49 + .../subscription/version.json | 4 + .../policySetDefinitions/version.json | 4 + .../.test/mg.common/dependencies.bicep | 13 + .../.test/mg.common/deploy.test.bicep | 50 + .../mg.common/interim.dependencies.bicep | 27 + .../.test/mg.min/dependencies.bicep | 13 + .../.test/mg.min/deploy.test.bicep | 48 + .../.test/mg.min/interim.dependencies.bicep | 27 + .../.test/rg.common/dependencies.bicep | 13 + .../.test/rg.common/deploy.test.bicep | 55 + .../.test/rg.min/dependencies.bicep | 13 + .../.test/rg.min/deploy.test.bicep | 54 + .../.test/sub.common/dependencies.bicep | 13 + .../.test/sub.common/deploy.test.bicep | 53 + .../.test/sub.min/dependencies.bicep | 13 + .../.test/sub.min/deploy.test.bicep | 52 + .../roleAssignments/deploy.bicep | 123 + .../managementGroup/deploy.bicep | 479 + .../roleAssignments/managementGroup/readme.md | 51 + .../managementGroup/version.json | 4 + .../roleAssignments/readme.md | 538 + .../resourceGroup/deploy.bicep | 481 + .../roleAssignments/resourceGroup/readme.md | 52 + .../resourceGroup/version.json | 4 + .../roleAssignments/subscription/deploy.bicep | 478 + .../roleAssignments/subscription/readme.md | 51 + .../roleAssignments/subscription/version.json | 4 + .../roleAssignments/version.json | 4 + .../.test/mg.common/deploy.test.bicep | 36 + .../.test/mg.min/deploy.test.bicep | 27 + .../.test/rg.common/deploy.test.bicep | 61 + .../.test/rg.min/deploy.test.bicep | 46 + .../.test/sub.common/deploy.test.bicep | 42 + .../.test/sub.min/deploy.test.bicep | 28 + .../roleDefinitions/deploy.bicep | 110 + .../managementGroup/deploy.bicep | 63 + .../roleDefinitions/managementGroup/readme.md | 49 + .../managementGroup/version.json | 4 + .../roleDefinitions/readme.md | 622 + .../resourceGroup/deploy.bicep | 73 + .../roleDefinitions/resourceGroup/readme.md | 52 + .../resourceGroup/version.json | 4 + .../roleDefinitions/subscription/deploy.bicep | 71 + .../roleDefinitions/subscription/readme.md | 51 + .../roleDefinitions/subscription/version.json | 4 + .../roleDefinitions/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 72 + .../.test/common/dependencies.bicep | 90 + .../.test/common/deploy.test.bicep | 244 + .../.test/encr/dependencies.bicep | 58 + .../.test/encr/deploy.test.bicep | 61 + .../.test/min/deploy.test.bicep | 42 + .../automationAccounts/deploy.bicep | 422 + .../jobSchedules/deploy.bicep | 62 + .../automationAccounts/jobSchedules/readme.md | 58 + .../jobSchedules/version.json | 4 + .../automationAccounts/modules/deploy.bicep | 61 + .../automationAccounts/modules/readme.md | 95 + .../automationAccounts/modules/version.json | 4 + .../automationAccounts/readme.md | 946 ++ .../automationAccounts/runbooks/deploy.bicep | 100 + .../automationAccounts/runbooks/readme.md | 105 + .../automationAccounts/runbooks/version.json | 4 + .../automationAccounts/schedules/deploy.bicep | 84 + .../automationAccounts/schedules/readme.md | 62 + .../automationAccounts/schedules/version.json | 4 + .../softwareUpdateConfigurations/deploy.bicep | 273 + .../softwareUpdateConfigurations/readme.md | 181 + .../softwareUpdateConfigurations/version.json | 4 + .../automationAccounts/variables/deploy.bicep | 53 + .../automationAccounts/variables/readme.md | 103 + .../automationAccounts/variables/version.json | 4 + .../automationAccounts/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 82 + .../actionGroups/.test/min/deploy.test.bicep | 43 + .../actionGroups/deploy.bicep | 107 + .../Microsoft.Insights/actionGroups/readme.md | 413 + .../actionGroups/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 28 + .../.test/common/deploy.test.bicep | 87 + .../activityLogAlerts/deploy.bicep | 90 + .../activityLogAlerts/readme.md | 532 + .../activityLogAlerts/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 24 + .../components/.test/common/deploy.test.bicep | 65 + .../components/.test/min/dependencies.bicep | 13 + .../components/.test/min/deploy.test.bicep | 51 + .../components/deploy.bicep | 118 + .../Microsoft.Insights/components/readme.md | 291 + .../components/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 13 + .../.test/common/deploy.test.bicep | 66 + .../.test/min/deploy.test.bicep | 41 + .../dataCollectionEndpoints/deploy.bicep | 106 + .../dataCollectionEndpoints/readme.md | 292 + .../dataCollectionEndpoints/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/customadv/dependencies.bicep | 79 + .../.test/customadv/deploy.test.bicep | 139 + .../.test/custombasic/dependencies.bicep | 63 + .../.test/custombasic/deploy.test.bicep | 123 + .../.test/customiis/dependencies.bicep | 44 + .../.test/customiis/deploy.test.bicep | 102 + .../.test/linux/dependencies.bicep | 27 + .../.test/linux/deploy.test.bicep | 215 + .../.test/min/deploy.test.bicep | 80 + .../.test/windows/dependencies.bicep | 27 + .../.test/windows/deploy.test.bicep | 169 + .../dataCollectionRules/deploy.bicep | 120 + .../dataCollectionRules/readme.md | 1587 ++ .../dataCollectionRules/version.json | 4 + .../.test/common/deploy.test.bicep | 60 + .../diagnosticSettings/deploy.bicep | 98 + .../diagnosticSettings/readme.md | 114 + .../diagnosticSettings/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 29 + .../.test/common/deploy.test.bicep | 82 + .../metricAlerts/deploy.bicep | 145 + .../Microsoft.Insights/metricAlerts/readme.md | 512 + .../metricAlerts/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 71 + .../.test/common/deploy.test.bicep | 86 + .../.test/min/deploy.test.bicep | 42 + .../privateLinkScopes/deploy.bicep | 117 + .../privateLinkScopes/readme.md | 433 + .../scopedResources/deploy.bicep | 46 + .../scopedResources/readme.md | 50 + .../scopedResources/version.json | 4 + .../privateLinkScopes/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../.test/common/dependencies.bicep | 24 + .../.test/common/deploy.test.bicep | 100 + .../scheduledQueryRules/deploy.bicep | 133 + .../scheduledQueryRules/readme.md | 338 + .../scheduledQueryRules/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 198 + .../webTests/.test/common/dependencies.bicep | 26 + .../webTests/.test/common/deploy.test.bicep | 67 + .../webTests/.test/min/dependencies.bicep | 26 + .../webTests/.test/min/deploy.test.bicep | 60 + .../Microsoft.Insights/webTests/deploy.bicep | 145 + .../Microsoft.Insights/webTests/readme.md | 324 + .../Microsoft.Insights/webTests/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 70 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 64 + .../userAssignedIdentities/deploy.bicep | 80 + .../userAssignedIdentities/readme.md | 229 + .../userAssignedIdentities/version.json | 4 + .../.test/common/deploy.test.bicep | 65 + .../deploy.bicep | 55 + .../readme.md | 195 + .../version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 117 + .../.test/common/deploy.test.bicep | 442 + .../applicationGateways/deploy.bicep | 398 + .../applicationGateways/readme.md | 1073 ++ .../applicationGateways/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 64 + .../applicationSecurityGroups/deploy.bicep | 75 + .../applicationSecurityGroups/readme.md | 237 + .../applicationSecurityGroups/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/addpip/dependencies.bicep | 53 + .../.test/addpip/deploy.test.bicep | 62 + .../.test/common/dependencies.bicep | 64 + .../.test/common/deploy.test.bicep | 173 + .../.test/custompip/dependencies.bicep | 41 + .../.test/custompip/deploy.test.bicep | 80 + .../.test/hubcommon/dependencies.bicep | 46 + .../.test/hubcommon/deploy.test.bicep | 63 + .../.test/hubmin/dependencies.bicep | 32 + .../.test/hubmin/deploy.test.bicep | 57 + .../.test/min/dependencies.bicep | 29 + .../.test/min/deploy.test.bicep | 51 + .../azureFirewalls/deploy.bicep | 332 + .../azureFirewalls/readme.md | 974 ++ .../azureFirewalls/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 59 + .../.test/common/deploy.test.bicep | 93 + .../.test/custompip/dependencies.bicep | 41 + .../.test/custompip/deploy.test.bicep | 80 + .../bastionHosts/.test/min/dependencies.bicep | 30 + .../bastionHosts/.test/min/deploy.test.bicep | 51 + .../bastionHosts/deploy.bicep | 255 + .../Microsoft.Network/bastionHosts/readme.md | 592 + .../bastionHosts/version.json | 4 + .../.test/vnet2vnet/dependencies.bicep | 132 + .../.test/vnet2vnet/deploy.test.bicep | 73 + .../connections/deploy.bicep | 163 + .../Microsoft.Network/connections/readme.md | 410 + .../connections/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 64 + .../.test/min/deploy.test.bicep | 42 + .../ddosProtectionPlans/deploy.bicep | 76 + .../ddosProtectionPlans/readme.md | 282 + .../ddosProtectionPlans/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 56 + .../.test/common/deploy.test.bicep | 68 + .../dnsResolvers/deploy.bicep | 117 + .../Microsoft.Network/dnsResolvers/readme.md | 332 + .../dnsResolvers/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 89 + .../.test/min/deploy.test.bicep | 45 + .../expressRouteCircuits/deploy.bicep | 251 + .../expressRouteCircuits/readme.md | 364 + .../expressRouteCircuits/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 26 + .../.test/common/deploy.test.bicep | 56 + .../expressRouteGateway/deploy.bicep | 69 + .../expressRouteGateway/readme.md | 257 + .../expressRouteGateway/version.json | 4 + .../.test/common/deploy.test.bicep | 86 + .../.test/min/deploy.test.bicep | 42 + .../firewallPolicies/deploy.bicep | 199 + .../firewallPolicies/readme.md | 337 + .../ruleCollectionGroups/deploy.bicep | 48 + .../ruleCollectionGroups/readme.md | 51 + .../ruleCollectionGroups/version.json | 4 + .../firewallPolicies/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../frontDoors/.test/common/deploy.test.bicep | 153 + .../frontDoors/.test/min/deploy.test.bicep | 121 + .../Microsoft.Network/frontDoors/deploy.bicep | 192 + .../Microsoft.Network/frontDoors/readme.md | 659 + .../Microsoft.Network/frontDoors/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../ipGroups/.test/common/dependencies.bicep | 14 + .../ipGroups/.test/common/deploy.test.bicep | 68 + .../ipGroups/.test/min/deploy.test.bicep | 42 + .../Microsoft.Network/ipGroups/deploy.bicep | 81 + .../Microsoft.Network/ipGroups/readme.md | 293 + .../Microsoft.Network/ipGroups/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 36 + .../.test/common/deploy.test.bicep | 164 + .../.test/internal/dependencies.bicep | 41 + .../.test/internal/deploy.test.bicep | 138 + .../.test/min/dependencies.bicep | 25 + .../loadBalancers/.test/min/deploy.test.bicep | 57 + .../backendAddressPools/deploy.bicep | 52 + .../backendAddressPools/readme.md | 52 + .../backendAddressPools/version.json | 4 + .../loadBalancers/deploy.bicep | 276 + .../inboundNatRules/deploy.bicep | 97 + .../loadBalancers/inboundNatRules/readme.md | 59 + .../inboundNatRules/version.json | 4 + .../Microsoft.Network/loadBalancers/readme.md | 1019 ++ .../loadBalancers/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 70 + .../.test/min/deploy.test.bicep | 46 + .../localNetworkGateways/deploy.bicep | 107 + .../localNetworkGateways/readme.md | 320 + .../localNetworkGateways/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 84 + .../natGateways/deploy.bicep | 183 + .../Microsoft.Network/natGateways/readme.md | 283 + .../natGateways/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 113 + .../.test/common/deploy.test.bicep | 110 + .../.test/min/dependencies.bicep | 30 + .../.test/min/deploy.test.bicep | 56 + .../networkInterfaces/deploy.bicep | 183 + .../networkInterfaces/readme.md | 402 + .../networkInterfaces/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 96 + .../.test/common/deploy.test.bicep | 247 + .../connectivityConfigurations/deploy.bicep | 78 + .../connectivityConfigurations/readme.md | 56 + .../connectivityConfigurations/version.json | 4 + .../networkManagers/deploy.bicep | 157 + .../networkGroups/deploy.bicep | 62 + .../networkManagers/networkGroups/readme.md | 53 + .../networkGroups/staticMembers/deploy.bicep | 51 + .../networkGroups/staticMembers/readme.md | 52 + .../networkGroups/staticMembers/version.json | 4 + .../networkGroups/version.json | 4 + .../networkManagers/readme.md | 987 ++ .../scopeConnections/deploy.bicep | 54 + .../scopeConnections/readme.md | 53 + .../scopeConnections/version.json | 4 + .../securityAdminConfigurations/deploy.bicep | 72 + .../securityAdminConfigurations/readme.md | 55 + .../ruleCollections/deploy.bicep | 82 + .../ruleCollections/readme.md | 55 + .../ruleCollections/rules/deploy.bicep | 112 + .../ruleCollections/rules/readme.md | 61 + .../ruleCollections/rules/version.json | 4 + .../ruleCollections/version.json | 4 + .../securityAdminConfigurations/version.json | 4 + .../networkManagers/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 24 + .../.test/common/deploy.test.bicep | 148 + .../.test/min/deploy.test.bicep | 42 + .../networkSecurityGroups/deploy.bicep | 191 + .../networkSecurityGroups/readme.md | 443 + .../securityRules/deploy.bicep | 117 + .../securityRules/readme.md | 64 + .../securityRules/version.json | 4 + .../networkSecurityGroups/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 144 + .../.test/common/deploy.test.bicep | 153 + .../.test/min/deploy.test.bicep | 44 + .../connectionMonitors/deploy.bicep | 76 + .../connectionMonitors/readme.md | 92 + .../connectionMonitors/version.json | 4 + .../networkWatchers/deploy.bicep | 114 + .../networkWatchers/flowLogs/deploy.bicep | 105 + .../networkWatchers/flowLogs/readme.md | 96 + .../networkWatchers/flowLogs/version.json | 4 + .../networkWatchers/readme.md | 416 + .../networkWatchers/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 41 + .../.test/common/deploy.test.bicep | 230 + .../.test/min/deploy.test.bicep | 42 + .../A/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/A/deploy.bicep | 68 + .../privateDnsZones/A/readme.md | 113 + .../privateDnsZones/A/version.json | 4 + .../AAAA/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/AAAA/deploy.bicep | 68 + .../privateDnsZones/AAAA/readme.md | 113 + .../privateDnsZones/AAAA/version.json | 4 + .../CNAME/.bicep/nested_roleAssignments.bicep | 99 + .../privateDnsZones/CNAME/deploy.bicep | 68 + .../privateDnsZones/CNAME/readme.md | 113 + .../privateDnsZones/CNAME/version.json | 4 + .../MX/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/MX/deploy.bicep | 68 + .../privateDnsZones/MX/readme.md | 113 + .../privateDnsZones/MX/version.json | 4 + .../PTR/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/PTR/deploy.bicep | 68 + .../privateDnsZones/PTR/readme.md | 113 + .../privateDnsZones/PTR/version.json | 4 + .../SOA/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/SOA/deploy.bicep | 68 + .../privateDnsZones/SOA/readme.md | 113 + .../privateDnsZones/SOA/version.json | 4 + .../SRV/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/SRV/deploy.bicep | 68 + .../privateDnsZones/SRV/readme.md | 113 + .../privateDnsZones/SRV/version.json | 4 + .../TXT/.bicep/nested_roleAssignments.bicep | 97 + .../privateDnsZones/TXT/deploy.bicep | 68 + .../privateDnsZones/TXT/readme.md | 146 + .../privateDnsZones/TXT/version.json | 4 + .../privateDnsZones/deploy.bicep | 220 + .../privateDnsZones/readme.md | 648 + .../privateDnsZones/version.json | 4 + .../virtualNetworkLinks/deploy.bicep | 61 + .../virtualNetworkLinks/readme.md | 95 + .../virtualNetworkLinks/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 95 + .../.test/common/deploy.test.bicep | 93 + .../.test/min/dependencies.bicep | 54 + .../.test/min/deploy.test.bicep | 56 + .../privateEndpoints/deploy.bicep | 132 + .../privateDnsZoneGroups/deploy.bicep | 53 + .../privateDnsZoneGroups/readme.md | 50 + .../privateDnsZoneGroups/version.json | 4 + .../privateEndpoints/readme.md | 497 + .../privateEndpoints/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 68 + .../.test/common/deploy.test.bicep | 97 + .../.test/min/dependencies.bicep | 57 + .../.test/min/deploy.test.bicep | 66 + .../privateLinkServices/deploy.bicep | 104 + .../privateLinkServices/readme.md | 666 + .../privateLinkServices/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 90 + .../.test/min/deploy.test.bicep | 42 + .../publicIPAddresses/deploy.bicep | 221 + .../publicIPAddresses/readme.md | 339 + .../publicIPAddresses/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 65 + .../.test/min/deploy.test.bicep | 42 + .../publicIPPrefixes/deploy.bicep | 91 + .../publicIPPrefixes/readme.md | 292 + .../publicIPPrefixes/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 74 + .../routeTables/.test/min/deploy.test.bicep | 42 + .../routeTables/deploy.bicep | 84 + .../Microsoft.Network/routeTables/readme.md | 395 + .../routeTables/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 84 + .../.test/min/deploy.test.bicep | 43 + .../trafficmanagerprofiles/deploy.bicep | 205 + .../trafficmanagerprofiles/readme.md | 415 + .../trafficmanagerprofiles/version.json | 4 + .../.test/common/dependencies.bicep | 42 + .../.test/common/deploy.test.bicep | 84 + .../virtualHubs/.test/min/dependencies.bicep | 13 + .../virtualHubs/.test/min/deploy.test.bicep | 52 + .../virtualHubs/deploy.bicep | 172 + .../virtualHubs/hubRouteTables/deploy.bicep | 48 + .../virtualHubs/hubRouteTables/readme.md | 51 + .../virtualHubs/hubRouteTables/version.json | 4 + .../hubVirtualNetworkConnections/deploy.bicep | 54 + .../hubVirtualNetworkConnections/readme.md | 56 + .../hubVirtualNetworkConnections/version.json | 4 + .../Microsoft.Network/virtualHubs/readme.md | 292 + .../virtualHubs/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/aadvpn/dependencies.bicep | 41 + .../.test/aadvpn/deploy.test.bicep | 106 + .../.test/expressRoute/dependencies.bicep | 41 + .../.test/expressRoute/deploy.test.bicep | 93 + .../.test/vpn/dependencies.bicep | 60 + .../.test/vpn/deploy.test.bicep | 135 + .../virtualNetworkGateways/deploy.bicep | 471 + .../natRules/deploy.bicep | 70 + .../virtualNetworkGateways/natRules/readme.md | 54 + .../natRules/version.json | 4 + .../virtualNetworkGateways/readme.md | 775 + .../virtualNetworkGateways/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 35 + .../.test/common/deploy.test.bicep | 140 + .../.test/min/deploy.test.bicep | 45 + .../.test/vnetPeering/dependencies.bicep | 30 + .../.test/vnetPeering/deploy.test.bicep | 76 + .../virtualNetworks/deploy.bicep | 302 + .../virtualNetworks/readme.md | 740 + .../.bicep/nested_roleAssignments.bicep | 97 + .../virtualNetworks/subnets/deploy.bicep | 126 + .../virtualNetworks/subnets/readme.md | 200 + .../virtualNetworks/subnets/version.json | 4 + .../virtualNetworks/version.json | 4 + .../virtualNetworkPeerings/deploy.bicep | 66 + .../virtualNetworkPeerings/readme.md | 62 + .../virtualNetworkPeerings/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 97 + .../.test/common/dependencies.bicep | 14 + .../.test/common/deploy.test.bicep | 68 + .../virtualWans/.test/min/deploy.test.bicep | 42 + .../virtualWans/deploy.bicep | 96 + .../Microsoft.Network/virtualWans/readme.md | 302 + .../virtualWans/version.json | 4 + .../.test/common/dependencies.bicep | 49 + .../.test/common/deploy.test.bicep | 92 + .../vpnGateways/.test/min/dependencies.bicep | 27 + .../vpnGateways/.test/min/deploy.test.bicep | 52 + .../vpnGateways/connections/deploy.bicep | 102 + .../vpnGateways/connections/readme.md | 120 + .../vpnGateways/connections/version.json | 4 + .../vpnGateways/deploy.bicep | 124 + .../vpnGateways/natRules/deploy.bicep | 70 + .../vpnGateways/natRules/readme.md | 54 + .../vpnGateways/natRules/version.json | 4 + .../Microsoft.Network/vpnGateways/readme.md | 371 + .../vpnGateways/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 63 + .../vpnSites/.test/common/dependencies.bicep | 24 + .../vpnSites/.test/common/deploy.test.bicep | 106 + .../vpnSites/.test/min/dependencies.bicep | 13 + .../vpnSites/.test/min/deploy.test.bicep | 56 + .../Microsoft.Network/vpnSites/deploy.bicep | 108 + .../Microsoft.Network/vpnSites/readme.md | 566 + .../Microsoft.Network/vpnSites/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 76 + .../workspaces/.test/adv/dependencies.bicep | 85 + .../workspaces/.test/adv/deploy.test.bicep | 291 + .../.test/common/dependencies.bicep | 47 + .../workspaces/.test/common/deploy.test.bicep | 218 + .../workspaces/.test/min/deploy.test.bicep | 42 + .../workspaces/dataExports/deploy.bicep | 66 + .../workspaces/dataExports/readme.md | 52 + .../workspaces/dataExports/version.json | 4 + .../workspaces/dataSources/deploy.bicep | 102 + .../workspaces/dataSources/readme.md | 103 + .../workspaces/dataSources/version.json | 4 + .../workspaces/deploy.bicep | 367 + .../workspaces/linkedServices/deploy.bicep | 52 + .../workspaces/linkedServices/readme.md | 93 + .../workspaces/linkedServices/version.json | 4 + .../linkedStorageAccounts/deploy.bicep | 52 + .../linkedStorageAccounts/readme.md | 50 + .../linkedStorageAccounts/version.json | 4 + .../workspaces/readme.md | 1468 ++ .../workspaces/savedSearches/deploy.bicep | 73 + .../workspaces/savedSearches/readme.md | 98 + .../workspaces/savedSearches/version.json | 4 + .../storageInsightConfigs/deploy.bicep | 63 + .../storageInsightConfigs/readme.md | 94 + .../storageInsightConfigs/version.json | 4 + .../workspaces/tables/deploy.bicep | 84 + .../workspaces/tables/readme.md | 55 + .../workspaces/tables/version.json | 4 + .../workspaces/version.json | 4 + .../solutions/.test/min/dependencies.bicep | 13 + .../solutions/.test/min/deploy.test.bicep | 51 + .../solutions/.test/ms/dependencies.bicep | 13 + .../solutions/.test/ms/deploy.test.bicep | 53 + .../solutions/.test/nonms/dependencies.bicep | 13 + .../solutions/.test/nonms/deploy.test.bicep | 53 + .../solutions/deploy.bicep | 63 + .../solutions/readme.md | 219 + .../solutions/version.json | 4 + .../.test/cli/dependencies.bicep | 28 + .../.test/cli/deploy.test.bicep | 78 + .../.test/ps/dependencies.bicep | 28 + .../.test/ps/deploy.test.bicep | 67 + .../deploymentScripts/deploy.bicep | 152 + .../deploymentScripts/readme.md | 377 + .../deploymentScripts/version.json | 4 + .../.bicep/nested_roleAssignments.bicep | 248 + .../.test/common/dependencies.bicep | 18 + .../.test/common/deploy.test.bicep | 63 + .../.test/min/deploy.test.bicep | 23 + .../resourceGroups/deploy.bicep | 79 + .../resourceGroups/readme.md | 291 + .../resourceGroups/version.json | 4 + .../tags/.test/min/deploy.test.bicep | 22 + .../tags/.test/rg/deploy.test.bicep | 46 + .../tags/.test/sub/deploy.test.bicep | 27 + .../Microsoft.Resources/tags/deploy.bicep | 63 + .../1.3.0/Microsoft.Resources/tags/readme.md | 234 + .../tags/resourceGroups/.bicep/readTags.bicep | 9 + .../tags/resourceGroups/deploy.bicep | 45 + .../tags/resourceGroups/readme.md | 81 + .../tags/resourceGroups/version.json | 4 + .../tags/subscriptions/.bicep/readTags.bicep | 11 + .../tags/subscriptions/deploy.bicep | 48 + .../tags/subscriptions/readme.md | 81 + .../tags/subscriptions/version.json | 4 + .../Microsoft.Resources/tags/version.json | 4 + .../templates/carml/carmlVersionTracking.md | 14 + patterns/avd/templates/deploy.bicep | 2283 +++ .../avd/templates/modules/anfMetric.bicep | 33 + .../templates/modules/fileservicsmetric.bicep | 36 + .../templates/modules/hostPoolAlerts.bicep | 45 + .../templates/modules/metricAlertsVms.bicep | 39 + .../templates/modules/metricsResources.bicep | 200 + .../avd/templates/modules/requirements.psd1 | 9 + patterns/avd/templates/modules/run.ps1 | 303 + .../templates/modules/storAccountMetric.bicep | 36 + .../modules/userAssignedManagedIdentity.bicep | 22 + 672 files changed, 87088 insertions(+) create mode 100644 docs/content/patterns/avd/FAQ.md create mode 100644 docs/content/patterns/avd/Known-Issues.md create mode 100644 docs/content/patterns/avd/Telemetry.md create mode 100644 docs/content/patterns/avd/Whats-New.md create mode 100644 docs/content/patterns/avd/_index.md create mode 100644 docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md create mode 100644 docs/content/patterns/avd/deploy/_index.md create mode 100644 docs/content/patterns/avd/media/alerts.xlsx create mode 100644 docs/content/patterns/avd/media/avdAlertRules.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesChange.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesChangeLogbased.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesChangeMetricbased.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesCopy1.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesCopy2.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesCopy3.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesEnable.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesFilter.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesProperties.jpg create mode 100644 docs/content/patterns/avd/media/avdAlertRulesProperties2.jpg create mode 100644 patterns/avd/avdArm.json create mode 100644 patterns/avd/avdCustomUi.json create mode 100644 patterns/avd/examples/sample-pipeline.yml create mode 100644 patterns/avd/examples/sample-workflow.yml create mode 100644 patterns/avd/scripts/Get-HostPoolInfo.ps1 create mode 100644 patterns/avd/scripts/Get-StorAcctInfo.ps1 create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/interim.dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/interim.dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/min/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/rg/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/sub/deploy.test.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/deploy.bicep create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/readme.md create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/version.json create mode 100644 patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/version.json create mode 100644 patterns/avd/templates/carml/carmlVersionTracking.md create mode 100644 patterns/avd/templates/deploy.bicep create mode 100644 patterns/avd/templates/modules/anfMetric.bicep create mode 100644 patterns/avd/templates/modules/fileservicsmetric.bicep create mode 100644 patterns/avd/templates/modules/hostPoolAlerts.bicep create mode 100644 patterns/avd/templates/modules/metricAlertsVms.bicep create mode 100644 patterns/avd/templates/modules/metricsResources.bicep create mode 100644 patterns/avd/templates/modules/requirements.psd1 create mode 100644 patterns/avd/templates/modules/run.ps1 create mode 100644 patterns/avd/templates/modules/storAccountMetric.bicep create mode 100644 patterns/avd/templates/modules/userAssignedManagedIdentity.bicep diff --git a/docs/content/patterns/avd/FAQ.md b/docs/content/patterns/avd/FAQ.md new file mode 100644 index 000000000..19d8b389c --- /dev/null +++ b/docs/content/patterns/avd/FAQ.md @@ -0,0 +1,33 @@ +--- +title: Frequently Asked Questions +geekdocCollapseSection: true +weight: 80 +--- + +## What are the Prerequisites? + +An AVD deployment and the Owner Role on the Subscription containing the AVD resources, VMs and Storage. You must have also pre-configured the AVD Insights as it will enable diagnostic logging for the Host Pools and associated VMs in which the alerts rely on. + +## What's deployed to my Subscription? + +Names will be created with a standard 'avdmetrics' in the name and vary based on input for things like site, environment type, etc. + +Resource Group starting with the name "rg-avdmetrics-" with the following: + +- Automation Account with a Runbook (for Host Pool Information not otherwise available) +- Identity for the Automation Account in which the name will start with "aa-avdmetrics-" +- Logic App that execute every 5 minutes (Host Pool Info Runbook) + +The Automation Account Identity will be assigned the following roles at the Subscription level: + +- Desktop Virtualization Reader +- Reader and Data Access (Storage Specific) + +## What's the cost? + +While this is highly subjective on the environment, number of triggered alerts, etc. it was designed with cost in mind. The primary resources in this deployment are the Automation Account and Alerts. We recommend you enable alerts in stages and monitor costs, however the overall cost should be minimal. + +- Automation Account runs a script every 5 minutes to collect additional Azure File Share data and averages around $5/month +- Alert Rules vary based on number of times triggered but estimates are under $1/mo each. + + diff --git a/docs/content/patterns/avd/Known-Issues.md b/docs/content/patterns/avd/Known-Issues.md new file mode 100644 index 000000000..1ec1aaf1b --- /dev/null +++ b/docs/content/patterns/avd/Known-Issues.md @@ -0,0 +1,8 @@ +--- +title: Known Issues +geekdocCollapseSection: true +weight: 100 +--- + +## None at this time + diff --git a/docs/content/patterns/avd/Telemetry.md b/docs/content/patterns/avd/Telemetry.md new file mode 100644 index 000000000..91eba5083 --- /dev/null +++ b/docs/content/patterns/avd/Telemetry.md @@ -0,0 +1,64 @@ +--- +title: Telemetry +geekdocCollapseSection: true +weight: 90 +--- + + +## Telemetry Tracking Using Customer Usage Attribution (PID) + + +Microsoft can identify the deployments of the Azure Resource Manager and Bicep templates with the deployed Azure resources. Microsoft can correlate these resources used to support the deployments. Microsoft collects this information to provide the best experiences with their products and to operate their business. The telemetry is collected through [customer usage attribution](https://docs.microsoft.com/azure/marketplace/azure-partner-customer-usage-attribution). The data is collected and governed by Microsoft's privacy policies, located at the [trust center](https://www.microsoft.com/trustcenter). + +To disable this tracking, we have included a parameter called `telemetryOptOut` to the deployment template in this repo with a simple boolean flag. The default value `No` which **does not** disable the telemetry. If you would like to disable this tracking, then simply set this value to `Yes` and this module will not be included in deployments and **therefore disables** the telemetry tracking. + +If you are happy with leaving telemetry tracking enabled, no changes are required. + +For example, in the alzArm.json file, you will see the following: + +```json +"telemetryOptOut": { + "type": "string", + "defaultValue": "No", + "allowedValues": [ + "Yes", + "No" + ], + "metadata": { + "description": "The customer usage identifier used for telemetry purposes. The default value of False enables telemetry. The value of True disables telemetry." + } +} +``` + +The default value is `No`, but can be changed to `Yes` in the parameter file. If set to `Yes` the deployment below will be ignored and therefore telemetry will not be tracked. + +```json +{ + "condition": "[equals(parameters('telemetryOptOut'), 'No')]", + "apiVersion": "2020-06-01", + "name": "[variables('deploymentNames').pidCuaDeploymentName]", + "location": "[deployment().location]", + "type": "Microsoft.Resources/deployments", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } +} +``` + +## Module PID Value Mapping + +The following are the unique ID's (also known as PIDs) used in the AMBA deployment + +| Name | PID | +| ------------------------------- | ------------------------------------ | +| Azure Monitor Baseline Alerts | d6b3b08c-5825-4b89-a62b-e3168d3d8fb0 | +| Connectivity Policy Initiative | 2d69aa07-8780-4697-a431-79882cb9f00e | +| Identity Policy Initiative | 8d257c20-97bf-4d14-acb3-38dd1436d13a | +| Management Policy Initiative | d87415c4-01ef-4667-af89-0b5adc14af1b | +| LandingZone Policy Initiative | 7bcfc615-be78-43da-b81d-98959a9465a5 | +| ServiceHealth Policy Initiative | 860d2afd-b71e-452f-9d3a-e56196cba570 | \ No newline at end of file diff --git a/docs/content/patterns/avd/Whats-New.md b/docs/content/patterns/avd/Whats-New.md new file mode 100644 index 000000000..2459bc718 --- /dev/null +++ b/docs/content/patterns/avd/Whats-New.md @@ -0,0 +1,20 @@ +--- +title: What´s new +geekdocCollapseSection: true +weight: 10 +--- + +For information on what's new please refer to the [Releases](https://github.com/Azure/azure-monitor-baseline-alerts/releases) page. + +To update your current deployment with the content from the latest release, please refer to the [Update to new release](../Update-to-new-Release) page. + +## 2024-01-25 +### New features +Initial relocation from the Azure AVD Accelerator Brownfield with AVD specific Alerts on a per Host Pool basis. +- Session Host monitoring both on performance, AVD agent health, storage, and fslogix profiles + +### Bug fixes +No new bug fixes at this time. + + + diff --git a/docs/content/patterns/avd/_index.md b/docs/content/patterns/avd/_index.md new file mode 100644 index 000000000..46629a736 --- /dev/null +++ b/docs/content/patterns/avd/_index.md @@ -0,0 +1,101 @@ +--- +title: Azure Virtual Desktop +geekdocCollapseSection: true +--- + +## Overview + +This solution provides a baseline of alerts for AVD that are disabled by default and for ensuring administrators and staff get meaningful and timely alerts when there are problems related to an AVD deployment. The deployment has been tested in Azure Global and Azure US Government and will incorporate storage alerts for either or both Azure Files and/or Azure Netapp Files. This solution initially was part of the Azure Virtual Desktop Solution Accelerator as a brownfield and moved to this location. + +**Current Version:** +v2.1.5 (Dec 5, 2023) + +## Alerts Table + +Table below shows the Alert Names however the number of alert rules created may be multiple based on different severity and/or additional volume or storage name designators. For example, a deployment with a single Azure Files Storage Account and an Azure NetApp Files Volume would yield 20 alert rules created. [(Excel Table)](../media/alerts.xlsx) + +| Name | Threshold(s) (Severity) | Signal Type | Frequency | # Alert Rules | +|--- |--- |--- |--- |--- +| AVD-HostPool-Capacity :one: | 95% (1) / 85% (2) / 50% (3)| Log Analytics | 5 min | 3/hostpool | +| AVD-HostPool-Disconnected User over n Hours (hostpoolname) | 24 (1) / 72 (2) | Log Analytics | 1 hour | 2/hostpool | +| AVD-HostPool-No Resources Available (hostpoolname) | Any are Sev1 | Log Analytics | 15 min | 1/hostpool | +| AVD-HostPool-VM-Available Memory Less Than nGB (hostpoolname) | 1gb (Sev1) / 2gb (Sev2) | Metric Alerts | 5 min | 2/hostpool | +| AVD-HostPool-VM-FSLogix Profile DiskCompactFailed (hostpoolname) | (2) | Log Analytics | 5 min | 1/hostpool | +| AVD-HostPool-VM-FSLogix Profile FailedAttachVHD (hostpoolname) | (1) | Log Analytics | 5 min | 1/hostpool | +| AVD-HostPool-VM-FSLogix Profile Less Than n% Free Space (hostpoolname) | 2% (1) / 5% (2) | Log Analytics | 5 min | 2/hostpool | +| AVD-HostPool-VM-FSLogix Profile Failed due to Network Issue (hostpoolname)| (1) | Log Analytics | 5 min | 1/hostpool | +| AVD-HostPool-VM-FSLogix Profile Service Disabled (hostpoolname) | (1) | Log Analytics | 5 min | 1/hostpool | +| AVD-HostPool-VM-Health Check Failure (hostpoolname) | (1) | Log Analytics | 5 min | 1/hostpool | +| AVD-HostPool-VM-High CPU nn Percent (hostpoolname) | 95 (1) / 85 (2) | Metric Alerts | 5 min | 2/hostpool | +| AVD-HostPool-VM-Local Disk Free Space n% (hostpoolname) | 5 (1) / 10 (2) | Log Analytics | 15 min | 2/hostpool | +| AVD-HostPool-VM-Personal Assigned Not Healthy (hostpoolname) | Any are Sev 1 | Log Analytics | 5 min | 1/hostpool | +| AVD-HostPool-VM-OS Disk Bandwidth Avg n% (hostpoolname) | 95 (1) / 85 (2) | Metric Alerts | 5 min | 2/hostpool | +| AVD-HostPool-VM-User Connection Failed (hostpoolname) | Any are Sev 3 | Log Analytics | 15 min | 1/hostpool | +| AVD-HostPool-VM-Missing Critical Updates (hostpoolname) | Any are Sev 1 | Log Analytics | 1 day | 1/hostpool | +| AVD-Storage-Low Space on ANF Share-XX Percent Remaining-{volumename} | 5 / 15 | Metric Alerts | 1 hour | 2/vol | +| AVD-Storage-Low Space on Azure File Share-X% Remaining-{volumename} :one: | 5 / 15 | Log Analytics | 1 hour | 2/share | +| AVD-Storage-Over XXms Latency for Storage Act-{storacctname} | 100ms / 50ms | Metric Alerts | 15 min | 2/stor acct | +| AVD-Storage-Over XXms Latency Between Client-Storage-{storacctname} | 100ms / 50ms | Metric Alerts | 15 min | 2/stor acct | +| AVD-Storage-Possible Throttling Due to High IOPs-{storacctname} | na / custom :two: | Metric Alerts | 15 min | 1/stor acct | +| AVD-Storage-Azure Files Availability-{storacctname} | 99 / na | Metric Alerts | 5 min | 1/stor acct | +| AVD-ServiceHealth-Health Advisory | na | Service Health | na | 4 | +| AVD-ServiceHealth-Planned Maintenance | na | Service Health | na | 4 | +| AVD-ServiceHealth-Security | na | Service Health | na | 4 | +| AVD-ServiceHealth-Service Issue | na | Service Health | na | 4 | + +**NOTES:** +:one: Alert based on associated Automation Account / Runbook +:two: See the following for custom condition. Note that both Standard and Premium values are incorporated into the alert rule. ['How to create an alert if a file share is throttled'](https://docs.microsoft.com/azure/storage/files/storage-troubleshooting-files-performance#how-to-create-an-alert-if-a-file-share-is-throttled) +Service Health - The alert severity cannot be set or changed from 'Verbose' + +## 📣Feedback 📣 + +Once you've had an opportunity to deploy the solution we'd love to hear from you! Click [here](https://aka.ms/alz/monitor/feedback) to leave your feedback. + +If you have encountered a problem please file an issue in our GitHub repo [GitHub Issue](https://github.com/Azure/azure-monitor-baseline-alerts/issues). + +## Deployment Guide + +We have a [Deployment Guide](../avd/deploy/Introduction-to-deploying-the-AVD-Pattern) available for guidance on how to consume the contents of this repo. + +## Known Issues + +Please see the [Known Issues](../avd/Known-Issues). + +## Frequently Asked Questions + +Please see the [Frequently Asked Questions](../avd/FAQ). + +## Contributing + +This project welcomes contributions and suggestions. +Most contributions require you to agree to a Contributor License Agreement (CLA) +declaring that you have the right to, and actually do, grant us the rights to use your contribution. +For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com). + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). +Simply follow the instructions provided by the bot. +You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +{{< hint type=note >}} +Details on contributing to this repo can be found [here](../../contributing/patterns) +{{< /hint >}} + +## Telemetry + +When you deploy the IP located in this repo, Microsoft can identify the installation of said IP with the deployed Azure resources. Microsoft can correlate these resources used to support the software. Microsoft collects this information to provide the best experiences with their products and to operate their business. The telemetry is collected through customer usage attribution. The data is collected and governed by [Microsoft's privacy policies](https://www.microsoft.com/trustcenter). + +If you don't wish to send usage data to Microsoft, or need to understand more about its' use details can be found [here](../alz/Telemetry). + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. +Authorized use of Microsoft trademarks or logos is subject to and must follow +[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/legal/intellectualproperty/trademarks/usage/general). +Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. +Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md b/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md new file mode 100644 index 000000000..1449a7827 --- /dev/null +++ b/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md @@ -0,0 +1,37 @@ +--- +title: Introduction to deploying the AVD Pattern +weight: 10 +--- + +## Background + +The Azure Virtual Desktop portal within Azure has a really great feature with regards to knowing the status of all things AVD via the provided Insights. However with the provided workbook it is not possible to create alerts based on what you see in Insights or having some knowledge of Log Analytics and Kusto queries. In order to provide customers with a quick list of common alerts and pre-created queries based on recommendations between the community with coordination with the Product Group, this solution was created. The alerts are intended to be disabled upon initial deployment to provide you with a method to phase in what you want to be alerted on, and verify the threshold values that work for your environment. + +## Prerequisites + +An AVD deployment with associated Insights configuration per the AVD Portal's AVD Insights Configuration workbook. +1. An Azure Tenant and Subscription +2. At least 1 Host Pool with Session Hosts deployed +3. Log Analytics Workspace for AVD with diagnostics enabled per the configuration workbook. + - Host Pool Diagnostics enabled + - Workspace Diagnostics enabled + - Data Collection Rule associated with the Log Analytics Workspace +4. Storage either Azure Files or Azure NetApp Files configured for FSLogix Profiles (optional) + +## Deploy via the Azure Portal UI + +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdCustomUi.json) + +## View / Enable Alerts after deployment + +By design the alerts solution is deployed with all alerts being disabled in order to provide administrators to view, adjust and enable them in phases or as needed. It is recommended to start with only a few to ensure the thresholds meet your needs and false alerts are not produced or an overwhelming number of alerts flood an email account. + +You can also review the Alert Action Group and adjust as needed with additional email addresses or other methods for recieving notifications. + +1. Open the [Alerts Azure Portal Page](https://portal.azure.com/#blade/Microsoft_Azure_Monitoring/AzureMonitoringBrowseBlade/alertsV2) +2. Click on the "Alert rules" section at the top of the page. +![Screenshot](../media/avdAlertRules.jpg) +3. Initially the list of alert rules may be filtered out or appear missing. Simply change the filter to include "disabled" or click the "Clear filters" option. +![Screenshot](../media/avdAlertRulesFilter.jpg) +4. Select the check box next to each you would like to enable and click "Enable" at the top of the page. +![Screenshot](../media/avdAlertRulesEnable.jpg) \ No newline at end of file diff --git a/docs/content/patterns/avd/deploy/_index.md b/docs/content/patterns/avd/deploy/_index.md new file mode 100644 index 000000000..df247e277 --- /dev/null +++ b/docs/content/patterns/avd/deploy/_index.md @@ -0,0 +1,6 @@ +--- +title: Deploying the AVD pattern +geekdocCollapseSection: true +weight: 50 +--- + diff --git a/docs/content/patterns/avd/media/alerts.xlsx b/docs/content/patterns/avd/media/alerts.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2a4453a210f3319b1b3e5071647df9aabd4c1f22 GIT binary patch literal 15463 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 w0s#P$nE!4&2Aj`Xd{5IR3c%e|z(qvj6}9 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRules.jpg b/docs/content/patterns/avd/media/avdAlertRules.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc53ac05c8ba338b0c3b3987baa5e764ba0b7ea0 GIT binary patch literal 142913 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

uHPD02!m+RL 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

iZGXdwg;Zb2UvDPw)F77k9ch2V=~=Q`6+4B+7p8 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 z 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⩔3d}QBOmzx z_{jffKsZPd&>cXm+7N&}iZ!LJTBb_aCLy4~g_>@8e;Eu&hTn|+W7l*0x=xoUx@w~h z^-5bbULmK#lMBx7Bu-xPCp~$I>#K@bkUu#ekhnyTo(O_+k+SUj*{H!c6c~3W(p8<=bBbW_9uFrkHE$&LBxWdAbO~a{pDqe zBdvPoS|`kO6ZXMbg)ZFCFxzaz0Vd#>QNj?UFTU9>cQD>5M~StjpPBxnrl_zFemu7Q z@}>SV2j7`L9t#R8_d)cKzh9K}2myF0n($b(=IqBka9C-0L%)xS-N(%bH*@XRW~yWZ z{0)ozUO>A602q3Fz?o^wi3h4GPp@PzD3RpZlUA*1q5NaKKWH(CPAKyKp%M4~QT2QR zs=W{mIG3^*OWuV=y%&Ag=eY`5ta^?Gl1_LFSn!%M#77V=vze+pz-$WE22(fmfCMy)~jkpt(>?u*v zp#;|Y<`w+oORJ=vANo)Hu_c%^z6$jenAWnDxE)*g`oO;eQB+hNbu&oCrFSz(dVG`1 zpc6`jNt4_$86i3_0@@ZsDznek0snkc>t+2V9@aou99qnsf>3^5FxcW-T==%)m+)aj zRC%BTi!Pfp{hP<6nGntrhxhhXq&nyB5XigU;b&+J&>=!aCzG?M)g=6nNf@)|<@ zM#%*O`}4=F1kw(PtC*X=i11q6h(Xnk#&jeRkHcamY$Iw}0$1&g-QeUFjk$@A5nbME4>)rOcKyl+@aeHbfbRb0p_<`LoR6z>dDgH(9p;vRnZd`B}0qf zJBB(>R@-d(nt{_so1d?^sf9F}=xyC0eODrF)ZhbtR_3v^etuaDPM^QB8VLv^cY;m@ zq5%^}IMW0#D1eKnK&2k`eO{Hhpj~Aaa3R`4ZR&1Z>xG!P10zquDHh$qmf^)K9`)2M zDct0uxNcV%aQ;x9W`fYyO|XuESeO76C%e?70{Eo+aVpfZ1D{Bx(D$keL~dG`>j^%) ztOj>`vbK%STEVdZ?zT>8FBK#0Qh_(br00yThZ%NnNOX#46+?sm@Q1t19?j&OO1f>M{1pzj62*K|$)1ue0pNFgRxwcnmMB!UW zran0VTbvxqkk3{;r0@NIH1z{#I@Q)Lv@};*)!Vxd5Bog--L0NM3?7PS=;K9fDhqU_3xGj7H z(8fYo(@{Mnsk$W5m5mBd8{orh#VeJhD9tJ#H;J<3E4!*TIjc{IXJ6suS8ye{Ln6TM zsIVnV+C1PYRMF_|M%;J!X=7j7Ys25nR-aDl-ka-Jx-Hr^-hybQJ0O`U)MMcI|HG^& zv!?jnh*)$rC-2=}fd!awl5XZ0l{P{bBlncIpt*@$ z!xmuaOG^7+LmhpQispkQOrYEhfe4k9GYzPN zzm8!!P+{Ba=Qj}*xf2a=M$d~BP$JTHjRb8^TJzq^m6JKoC9rJoI=n%k72WEo6P&8) zvYJH0`x}M2;SXLRvo5~ibX)Doen$@D%NH9KK`V>nqNL4mbPbFXo8`uR)BKtA8d}E- z^s!majtDR=#uOfXEw}WO`vw`cwuBs*`;;N*;LAvk^L_eMFJ7l!UjjvB^)so!odIQE z2mwB#jzRqpv<6y>pWXUyZ25BwftgzD>z}GfY%vcZ<0RS7fHWkWb<3W8pWDSkrItCa zz{X)-n_CNeXvOR=+gsPx7Nw7R>EgU^!tHeQ_H_+NFB;p-HKO=cIWleLRj&2n-eixY0Xa`W^WHZj8y=ui!T`!GHH_3TggV6{PU7eE zQ5i53MfpDWl`a&sE_3U!vPvcW%(GHf!;!cc<^HQY5r$ZVG!SN^G5DwWq!&ri5OD|g z6`Dgxkb^ch`+oL4^nHA${nY}|(`ECPLHc!4;sK-O@_}ZJUhWQqo8z1fhw}QV%$nvD z(_qL%<&&X3RsD*eNL2aRKU-%alVQOKe@+Qf5%hbDELe!J)|tRM$uziD^fBzNgdVq; zzjSJz{Oon5Eic-hd9!8Kgj1*w_zClxGu>u|V(u}E%}!xJH^)+K)B8<%zfN}6y2tb# zmFih;t7L~> z^~7wG=PIoWwf-@6j3n4Jj61c9fwy>&ev(U$mHCx~A*mSFiywrIG8Q)J>xRBhB{O*T zaFZrU1j+9K<^2qoyo)BYNVkjigk2=Nm&V4=L>#*W^Q9+H@K^aFI;Sd?2I*oyw)ch>qo^ps

h>RDuIi0$^I6`w^pcUK3;=_!Bj^P+XsT_WAgLGgYG12{VGN$7gG> z@N(m+-HGzi9TGfq>SyDVl)<`9QgJV;l_kXR#nO}7;V*5bFMMSd0FvL3vxGX``Pxy* zJKT{mErSF(^Kvm8h>1o}+likRhuz9KMW1UEZ_SpheW$Sae+QQT&2vinn%7j2G;|F_ zOvrc^_0=8v@-OunM(dh#2jV>I9@C9042nS&*B2R+`_Jo_m3`ew zy#^X;>*I$G7+mes1H?8$kQYNb$Tk3WmCvU#8h1D(&dzR%!XE{A?%B=C07!bi_5)ip z0ewE!TQ;u?s#E^vX|=K^^fLzCAGxi~HGRe7I1nf2k|D?`W@tf3JmVWDU*D;$I9`?T z`hn?`W=L|}@|UTr?T(Hog}%$Q2Sg?;emZ=E0zN$%LLduuCMwQCyWxOqxNt4tW=tdhQ8s{lk7%OtH-_{AHH^-zfyaH5Vi#k9?pTL4WW?DujR$Uu7eBk8cZUmL5KnSj@=o_^TnceTO#-WS7#@=K*p{U@K0b;U5Ro<2$O zkhseJG#+cWl~bU9Vi#k<{7%PnMF>$`DWF;Z8$S-C#Vq+P;ev_SjXG-2M$np5X3tb_ zxYt{cuiY0t!ozi4_b0d#6#Ry~!bSn1xoKkyMYC|`e8JB}Ab-LbDnKW%e&;tEP{Vx3 zy9T(jIRg!W{reuy765W1qeBOM3;}3s#G?3paOWqO9&_9tyqTwuMp(#)xy}X6$E)aP zmqim22I-NECRiMi&>qiZi+Yilut!&1s;aO*Mfs{bS0%vd`f2=p_jmUf8?GN8`~=uD z!!uf#t1f9>O+l;Qd$)$!Nrko07{IRw>WfK&$d9=UF2avJG%o$!LlM1LVi-8zDVs%( zxcU}iyl1W2s?N3}&D>&6sHRAH%*BhQ!;Iyg+ zak-fjC;MQU9W0n95l)q+pyKRkY*oV>o7@dx`0(uEhBqCcX*%E{Yh2$jmob(7cLd-Yy0Di{Du?qqDssvtW*g9 zZ0fyG*-DMNP%~;@;Bs*eYk48$I*|6R>Ct*_GK=WzKZZNgT`UK;%O!Ij`{+j7thIkE zyd8Wb)7bg-L7}5HP8Kb5qdGIl%^%g3OA9xTV}13?FP__7JooBA<+o*BIjUFr zUU?yxV;-vJuFy0-(Hj;{l&PA9Pc&1|i2%0s)KZ3RlQ;6znCjCin2=#{YyH@}uf%)> zYe8{d1C`JfArl5ZfmACRhvP*Y;^g+cdRjkN2gES`Xtcd{vW5S7v%g-RC~G@gBjSA` zr?mg5awh`Lx`DVvSID6p+r~t^@QtD*J|^0kmvUOJceQ*@IVj=oBXRjnhpr>&{dT1u z0A249V%h_QGcPi*T(C7f^VajoqVNJLGI&DQa>3^}4mQPV^lkCVa_mhuY---D-Cy7F z>LUTDfkk)`dQm1@6=$;zsbuEO2q-dMGV54iuiSGr4fQQ_#`|jK%e7y(!4fB{)zACA zno0Y%cv!H#@bp6IVTvxJb%a?abDp| zb!INkT-6#l>dcfS%VQ!h{0jUO82^em(y*)3tz&R9b1p$_cy8%{xNh6^r+QtzR*lc8 zUNE}3t;6I{3AW_NXOd3;ZUfoXtuE;c)B~ujPLv1E!s498hO-^%1BroN*-BWGPu2EJ z-}9TAUXzYjc~f|1-Ub0XLJI@;)l&YxzP^~UutG6N_gHv5AY0XY<;-`v6k|x;c5Wui z+xZqYlU1*TNq)Bh9T}g`VsX|_JiU3J?f~a2GeSg%KZcNe zm=2H>TQ8~Sg;sHaLTT|`t<2ipn#^CN1L+A){y+Vacay558sd;TKs9E@f-)7!84U0^ zo-BcHZsCrwfYPsaZ@sVK$8T+)DS}`Bwc|q#R|5Q|Hi2<}0~<|Ra4~y$$FxL;I|Uye zo(m&U`G|O`5bGNI1f9B*h%2B=J{}6b z;ei%^j*7{>#vCAr#jZ=TAgg3}3_{LrIL%%I(#Ob&Q23=0eA?i$zcSeYMSQ`!%e+M! zZX-U8OK(bgxwA;9s0&p?bmiVsHv07Fw|0bG0CrKj#<}Pb%b%%uhxrjA=Ycv{y9$+Z zv{;0BCSm$6egFFSX|zcE6AdE`{)o<_zD2Ru+E_yiOh*h{QEaWA*@y{Tip{tZjXrH3 zzER2YpP;{xZdl8vAL@h&uz@;?sn|g*Ob0+^++>P?gL(C-ZVLZb`L{|%lRxaMU;h!mnI9!)edr~z1x$uhHAwwt_0{fr}^xyybSj3UEMfOwbu zFY!H(9+AUceixy>Rd^sf;NaBNemyHMrJ;qOJ^CX&iBix&N%~P~m1xkD4u%t6U@lcCd7il#_ z|Fu2vSJmwG$S^{3$kuF7GX?XOi0)j~D^&^1)Phr$FJ4uclogV{&Sz!iD8naQ<6#{c zW-^q)XNRAviwAM1(5Llz>5+E7uvazvK&>xCO0+ZK zWWMLke(sDf-&{6HbEI_se!ez+m!dG+*Ep#q)Cs)zK!S*jRIQngF^(A;SlUz4+5BL% z*iL-*{&?_{h|4_Q2O^XgD@+-UOdksPY)Qfn37GX5k03%G`YMbR5Z2FE=Po_ZTx%EB z)qiKV*r=FAK?t)PL+~BYT0k1GE8IlVDfq#th-tWm`TNkIU6IPXyZj^hW%)<+XaDS9 z59sSvv>5-+nyfL~U5HPrTxWS~xf$xHe4x}~?VS^$cZzd^2`<1m=T#8u`)y|8-#m}i zO8(}_yX9oDwo2DnB@3-7&tlF3lh%e8qa_KCoUWPvzCAc$&Y*qDenieZ+vqrVl=#fg zkravY0Ucz6fo?#ez5op}+~K#nAKsO=7Sn0zWRW3emL#Y6#4BAv>yY2XmO$Ms@qhPX zt^e;{EbPDZV%`4f#a`<74}QhE3=1?=+*Rb-$7$Q(L!s<6dFD1pP>;gfDS)D&6SP2Q znfZV7uq>QL#3dFszP|-sF1rN&E>V_nNohtfZCkMuwr7ZLhp{BIK%8=zs%jLcQQ6INoAm-;rwE}ytg(JPZ``i@8Fj1+-==|_e8L8l z;Oh2jR+A=V8C7&bE9tF7P})rBTCLp~6JXZ;gmuEwxKuack$mujY5V-B<3)by*Ub$& zA*Y7)6@doCUE0qqv-d$FP2HIa62x-9a-B7TVyq_o%_9_J!IbNrin%nA2F{xQn^ul$BrvqLj1Igu26I?sjcb078M)pa}EBD<4bA{1R_U_4wRQhI1p zb4oS8w*E+iaH5*8wL#bn5A-4&?lA)?QoJ;!w2U;e7PXC!^^&Zi@ z6}dZb>(v!6;{7zW%F|}6Yi@lugK5b!c6UC* zgnnXc(eWKiY!tb%?#5?(MMh`S^YjK8yIawTVrl1|OCq-d2So3)j7P$y{>=F7jE~~7 zf-lG2e+Dnen4EQGj_+gS20j9f?Z0_sV5{f@2vsurl{1vK5dl$Z3-nWHELzp3_4r|o z*6$izoKH;qcK@Z=PTRQy=p=}?V@TO5r6IP6-kqz77pa?&HW__v_p0w@imO5Xo8+sSHBwQ-Hpd|Ozq;q^8)d=1|{ zP;Ft;9Cq$T>9$)t>XaGlDz|3~ai7*w^ohP3t8YL&D>2no{d3=^?6%{Z$X9up$<==S z1_8CUPKrH3XV`w6mpJf>3^;{!Z)^=v9UiZ9rxv{O`YZA0-FT%$Yr8y^1KrOi_X&|~ zQBF|>PKFI7KtN1ITE9Yjxbz$Dc>W+cLk(F+zif+9rN(8=}UksFl7o zV;IXl%d{njqa(4`%N-pY+vFr_Gc>y70_0Nk2js#^+$^8b*JGrNsVdRho+W*Dgn@2w3Me*K>I+^Z!B%*iaI!Nes0y?iFh;zw|JAPq;J| zI8WFcXmlNaYyh4w^%8|=k=)_z&XmY+o7FuNS>*2d3FX>T(;h8b{)#RBl@o);iziPw zwYnhvD=e-};h)>B0#dLxRmx6riZx z8!z2GFEENwb6H3{2@ix)mAa>tvyo@j~cZIqRHgNhzO9Pen1)^HL!wqDmdrY z;p~0?otasmjrwC*)D6n?aTHY_6odUSkJP>)-*%izuEHXprGDM>7s;w=b9u4U;K6@p zt&DcL?MN4jedav@ytRTad5+cX+2Z|Ik8XX&@Q-2`rs-o~u0J_~BM))f0%+7zxVP31 z?bZ4n)wR?S*6Gt}cNt8KOQ?Ot$GLPBcmWv$y?{`sp#5NrIl3mGIk!w?kxCqwt!u#5 ztyu{VL3yU3FS@UMCBOp2#4V3t0xAZ4c7G$0-D(5LMVjjw+tB5nke1^u-BtQv6&6+< z@r9GW<&Y?aVL1VTeg$V)u77Pm9lbf9xWPGynn+m zAYSA0f`MT`f_Tlo8z8pDj_V2{AS75Qpy>UM76_DFxr|Rww4%s`nZdoF_~zR0k59pgAH z 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

o?mpZ2R(c;9K77*Zt0Iq*rp^=%+o19lG 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 Glm7)Xte{o^ literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesChangeLogbased.jpg b/docs/content/patterns/avd/media/avdAlertRulesChangeLogbased.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3303f7c11fb85bf31e1ec6324558b4588b6d98ed GIT binary patch literal 333532 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesChangeMetricbased.jpg b/docs/content/patterns/avd/media/avdAlertRulesChangeMetricbased.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f48f48303feb94026067b66cb4914ab82ce28639 GIT binary patch literal 184866 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

-M{^6>C31B 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 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesCopy3.jpg b/docs/content/patterns/avd/media/avdAlertRulesCopy3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7ec675b5f3646901ed5d7b17b79962f02d0b7f4a GIT binary patch literal 102466 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%0AdQP7t)y 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 I@XxV-1IVtI2LJ#7 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesEnable.jpg b/docs/content/patterns/avd/media/avdAlertRulesEnable.jpg new file mode 100644 index 0000000000000000000000000000000000000000..488d008d2e789277977e458d7b26a8d815122599 GIT binary patch literal 160350 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

c27DHk^g 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 ~SLaB-i@(xDY%7NxKY;SyQ z;3Rc?cy7g82>8-U(3aVxs{zX6^Yu%j0_{EZqS5^7K5DnijdBami(8x6;tr$(FilCO zGskrg(4IP(^2^r?Wyak#9S<5IV>JHj>cMA~uA@leV2f%Bb11OnUNfmqfPpq1XkD>z zFibMLd&|-I*MhnD=V_-IZmOak#`+IWNO=j$rS~-0_1y!3>CiN(x2sH#tmlRJNAoJV zhuucA@0nO*=7n|>K3JFA9u+h^4o>zYDBgjnO^qT6B6U}{eXksB#hLtd)?yyC}04~!<1+-uwvk16>c`&a{6#m*+Q zeEP(2dc7ODP;*>m)NyW$YjRCtx=HU&1?}3$yh#lU&AX56R=U9YFd!)`a#IOPa#J(^ zHjw5o<&iyQ8x{KJGh1sT{_O9#cb)(-&9MAis6~(?k8NBwQOXyz)B}FQB+9+O;8>f> zebZrHGRk78S0@TMIO-bR+H{wE^}% zrL)sSHh~VXE3F~&D@`u^4b{jOqq`b(39BT#CX^P5fDU^nNZ!{~4}Qd%~vrh*zKue-_mmL~^Q* zs|}7o8fh;F*gD7-yV?n2lwoPuK&mYA6tFjH2mfXvJt+=GyJZtJh~1rSnd{vB3g&FH z%5qPKkT=SyjYeN1%v1WE*4N80-V15agzXatulX8x_W0jze^OFr=2n;MUlf`&i^21` zy{d7Yjdc{Z#Y)@1`kc#|;GdX4cdZMw*dc&McOl0QNhxt8mK)8dbcSRUSwv%keLzT!E1(pQ*9kTd>>_>HfkAt_)^D$=Hem_obK7Zh$;F#}W(sSsf zdsY8S(lwKYy;R}y!9ptE$DgPm9*&#yauN_5aOFbQQ7I@89jqS67A@VY2o2*u(-b~* z4ztNm)>HB<>6VyaviW&dpZV!7<^yTJ>I=f$3iqoTe{tp3X8B7wIWGdhUK_+Hy}r-> zX6I^XLe~(cz@(@IQ*Kf(ub^yAT1eQftrL+?t zXjU1!&&ymBfd$7r5SdY+zYOYD{_ncX|FfjE|9tG!S2DH*FF;+>WekytZ|K@2KE;3! z8|h~8p5CmKXw0tag9ncW0_)3wRLiWnX^A!CQ5-L|?&+}GvtG}DfShTyi+2>N8Z}PF zdAR@p9efl+4j^_nDdsu>O1ZN!dAcRvUk&db8zID}s*AWj4Yp>+vhj}N8Dsns6!-_c zt0Xs4`5QH>2w~iEkZK6|*$|6W3bd&f8IlX8F#mocf9SdM@Wf2!aOJ`@F=UmvWY0|- zif*u^XUiQ5@QfQ5BPA)aiR2w^K-!(Qh=7;@v9J9=>pJqVN;7M1F?{A5A;A7r?4=;} zuJx)}>@|dF2VtNM9L>p4995EfNyy(%l#9SfkHJj-GFb{42-a>hy9WCFz&;yd@cm&Z zAHNB?0e-La!hROT=8U^FBHA!2Q9iTS?GSHt=ea?;C0BySxz^Gvh_Y3A4e<__`jBOz z3>tPv1Me(x0QG{?ahh$T!$2$mK9a#F@owhUFutm@zM!pi1?W}BLKViW5517le#^{@ zuJs*O*PB=@Gt+l_L)#dwBKVRfHRL6>{o-ScC@_w|k^eCGvADB-7% zL^q^nZR|+?AHa@K7DUM8MqppYWLQ`B>MxTz zT*OFcD5=&E{+CHacMtV&AquqG2>*=-_*d^`*iKPl^X`clx?WWjAVD~H^k=vYCeyBC zr6t%bVfg7qgQVT}mabkovnq>eJvYxAYDyAz336Qa&pn>-e;%{$+26vJY;SFIWMS0R zg8@1B1e@wOj+q)nN{zzZv80N&1x6p39fci2_7|WvcpHnTI?!A}3hc6U?!yh?`M=aI8ql;gEYS<44K^G1778TxPV~qq6KT;aTanf&696Pf@=GDo5Y5b+dkV%=TkKF-hzFt0SD&F9zuGBzg4OnR@6hO_5 zcpdmZB7puFP@jWi@w*}r)ZCK-)V3aiKj(!?a*_3fjcBU38%Ry;?4_EKk8L+U1~A8W z=#6fS+}rSKhK?(NOArq9-)lT>OUZ$kg=NQoDLpvtdoc!JyxZRlonluHH2*FTV+C(f zJT@>^IXg%`FAl`#qFEN|VW!5YZR)B6fxjV((56uqeDJ!@Ue9RDlSN`Mt08h=LEFey*c7zk^Uqa^B%;Y6JMDhDLP=Y~Xd1MDM1 zwz;+}_vM+ROVHlGuc(;Yzj|4Ap)!+Q<6IdA3(xND-upc>02NnxX$V!N{%b_m#I=Dh zB6>*x!w(fv%eb@iYH)q-*Yc=+I@fQIu4qz^Ah%%bzhcbWsNOVLaQk1T$Qkh=>1>Xb zLF&U+5+qBrGxd(qsQBeb%eSu*`cH_Ztp||GF_w>TsfUmtT#B&t+y*BBxmD9h?A=qc zYRKjS=4`pw)zC{WN1JK;eBkz!Z1_;Y4k>Yi{naI^hOj~`@2L&jig~t)#I_i6qcvr` z4g4NxC0yPmvi0)7WXp5)3akm5w+IRE;y*Q99ic=EsKw6jr;z*e=OrV+m7SrvgqqFL zVuZb$o7-^dk1d{}{e?ZH3!O&DU+>H?+QF1WXaZdgR#%fj1DMU*G8}t8;0owFM~Xik zv`8i9;yd)hC8?Uj8sA`&HDUMM7}JgJo9Ae016%_f%c%3f=r}g-O2tXmQzWXKXC+w> zDrR-#HPbo|#UM|43*&k2^1FtG%Z9E@C-Ir#vy+?s{xU__{Mz3mO#5z6QLI3JroprL zmNfwmgvPQyb&a(q-+EPqr2yFJ*&^J<7an7|WH%(1wOOwt4o%SBZ$X3L+0W2Rbh)yS zUk8I!c*g@5w92TY^szkh24^hg-mUU^tqJ!Jhi(aihpN%=we56qa(<~hG4+snQ_bh(X)Uq>*O=y zet@B+l4yg^pOT>`H%xC7EN25^Q)pDk%|B?7(cMdu8%O@(Wej3T-+p8+ORtkAM4n3 zuBW`_@`t-qtM|WOMW4Vz22u9emz+>+O7f)asO)RyA-v%Cw`sQV`CYksCDwf|p0xp- zqEK;&JfGLOt&hrz`>o^%`fWTQ5_Aw?TS4E9Fw}B`+f|MG45{kGC|5)w4o~wwsD1bm zF~D7(BxAVtGj(=rXD~$MK@sT?HvsbAbs>Uek=`W9i);LBqsv1z8zQuSMsH3o{a%$F zfGY)@2w(8vdd04xsS1+C6^wxGn_;Bu8_DH#(J{s?B21y_{$VAe(5N=(Jw}!w)KeBG zugoR#G~$H!hA|bgFEAJ))K>1Sa)#RSg__u9#Z&5HbuZm@zqaD_oG@Z@ie#cO@Uu9y zORr5Hv|X%H@|Q{CFVpg5ah){8*~jm2{}6!v=c@+`V?w$aTq7zx2z&>am2uA!`M8lB zb&t^bDP3ty@s?0{3OC=6WQ{R-rjyq)cbT)PP@1d-B5^Jce;FAx{{b4Iav`YdgQQ9M6I<;*{;bfdgjCtvusAxqduX2K4Fu{b>rZBbnMt8Y`7xZr3c)`{ zLLy|qu2ABQbg9VJ-fd?yn~zPI?C&a0q}HDdD_w-FmXEFFM^xhV7zPZk!z+bfzc!4* z%h$`w3!0j~#lHxjnP%w*RkW5AEhK0$vFgJJA^ePfzFYKsQT_nm!~$(h&7DXAl7GA`8b0gEbAox zjMThup6Km$ct&gSTz{a6xPX$S;HruK5t6#YuJ1v zo*Vn%;3}s-Jn2EvjX3f)vmiv{IKGL6!ar|37{X0=tDs~@Z#+*xNbAO&43>u@D^e{S z0qo>&W?t{{Hwh2kC9QOR8I?h$2EkvZS7!z(B56>&#Z}M@jya_F6kw5S17ZRYh0iF# z*wFH(yWXeG+l;CmVqSV7#~oO?9-MjfINIiGlcS7WyTL@WGxCs7L}e^C2? zW#J!f+j^QF>PN&2z(jQCFVn6`%>wHG@#Vu5U=b}WdzB8_Njoag`^zMRc}O<^AX9I4 zWSndeDAS6;bzfN4gKeTN&iTsFkpH=2+ZRE35 zMc;CWx?Apai~&s)s(1g&P+@YD^xzh)(heiPf`+F~EdfCc2K4BFzf6I}2YSc-pgomS z6ob$jKvC_IJ%JYbwI|883gLuStfnMD^u8`%ssKW79zEKy*xPjsM>%Fkg1J%7W>h#u zv^_99i$q;VYh!&uZ_1uVeLNiO`eqP*`u-of`w9W$Oss^ytI3XErzc3`? z=dSrLoa}M3bl>$39)UMak`&f~PSjOwjm$8n>7ipmFRvd{X^Ma2J-e3~k9~MC5X>+- z7Cdo%E)V?>3fK|ao;X9vXzq}XV>C|Sn=IIMWg)og=R_7!!f$W>#}5O_8-_qsG7~?3Iu3=y9F&evqZ<>))~K|MUdh8jofRyipm- zu=icT;2?nK!9>NpWFuE;6VSLn`JW^d{FnG?+jyEZz_;k6ivf5uEU?iS`cc}qhM}sF z8lflXmSltX@Z^~;jj{C_?lGwpsWt_ze+95Jc}bfW+9FSksf^0)L*FZ1Em@fynAk$i zNez`F7Y2G5PYa71`IelIR_s%-NVZVBKUZfTe!}+AfU&h3 z$4XOcQ>iId)Zb7>TpDm{i;EL}?2Mg_%DuqiR6QP47(^Vv233t91t@;wZ74QL>40*K z@Yu_%1!+ES{3gqKPAw#U1OVFnun4ky1zQBtGIuoWibJ z?WuasAV>$xL#dbfNhvIelj#de@Oj-h9v8!>WKBCe7<*mS*U)2|Cbchd#;ZY$$KKT@ zq=y%NM=F>Ze(-k@8xIM#Y48zcOC4(K*5A|vff^?Qd~6%T{P-VG!m=Zqq~^pG2zQ5q zZ;xq-I-9CiJv@0~HbIl`J4PjHu`se&C+Sa@GXc2Be0)c8FC>O(!_N{93-BVoxT_Rj zbm*KU=ZgR3{sW#S_Ks5;C5AfkcHa`taVftkzr@y=&Y3fJ1I3D{>_39QxNRXfHtrW@ zvob6%;Lm6;mwT9)>)mUu5&BX_%I$T)q%V1mGiLk^ek~Qh&Nb}OU#ZRaSs&KcSX(GR z$Qp>GJLam-y(5o@yKdba%KLWR+}|qn?zei6`z5N#lH6gm=Su%_INfa)b#m3w>Q_1a zpQD8Ko)mK<)!O#2H^c1@(=@!!Dqh$w0Tpm}-p(>`rI|H!UxiJ{Es<-iZJs$pP=p_QaxvlY^#!1c27NTST&%c3108P^(Oi+lbAymX+u<- z43CipA-1?&h9@(w#~Uxk%Ik*ghPJtNO`mr7Rs!N$@}1Y0-W`&-UfYB@3X$ln{BD@E zD2EE@)Pw4Fj-d|4@Smyn2w`o(mkEMr&G?@1T?(|t&I838(yB6~-ArK`UHhlh_c-B& zlLYtU8w~Gcp%a;oh4Z#{>UI%bCT*=ILq;E*2|BB5SWNBCDlD>9?ed+z+B|@!n}uE* zRzKj|*y{L@GoK}DGweFlGp_w!+_g+Mh2`Yi3swI2xGRH?OXwlDscw#?m^yeKtQ?UL zoFiSAaM?+yUv*1B<$Ex`wW&X9E!Hziug5QOX~|rhVBpv23WPu9^xeaN(X ztkX94h~}8WfG>0{0Yb>b5nrDaNOO?>w_1a$BC!OmC)SJM>?dNE-;8}zxP!SsE%_GN z%E5OChi7>CAJ+OAwi)N#)k&|}pn@d7Z{RqojauaLv2W>9{*{(4fvycf!Ec`ZXzW>t zSzvvAv=a+58fYi7H3=)M^6K1!A+ovmDh2EYYAFlOz`k2*JS!>*WU^F;dg4RTT*D^mcN>%}OySqHX<4D!Q9{Qq%IY1rT`aZ3#wqnT560_l)a5&Eie&dNwk7c)x}9-NCp7 zgavvo#?*NoR*x_Ara)Dl)eG|iLd*?zH6cLy?N|ugh%fBJ@8x8P-QEvE>|zOg-&^Db zW91p9IiIP$PjKXD6<&%>K0Rg3sA){D;e~;M_+O@g|8su9z}|kz;%%)tR|I-AQ7Ypz z>_5Kj(0rlvb*`0uZbM>KgP(J0K0Fcjb5+L8PIbsvSz@{MnG3tNy(oG-Fe4{H`-D;M zOJvD)=6X?oWl;WTlL7glosg71Q<%S&LdaY0tFNp`kgV*L^x~qga&3DkQf%$joq2o8 zOme%gb8b0uyc((*+9l#4iT7}s^-$@g1Q&Y$hO!m>s7{w}`7P2eXM(C}K^~9%JaQ>) zoif#>lJT`CLGmT<>nB`=e^efl=`%O7+f;5>kBAJ;H^53+N;wUxzZ;y3*Om? zJsIWE2W!!g)w{F3S{JkBI5Xh>1#P3@Zq#Vqr>j|=P&4l6M$0m*C`AP6HVuoCGkKD{ z3A)S8O9d;Dz(qwuFwxqaqKQC~w1?MaP9-DFG1VqrGqV`a}}?UowhVB^spm#tNxa>-X1Mz_y* zy3Q|g*WMEj14wY3W*;?L)y{ieh;R_Rd7^uPW_}&Of10(AV9yz&e>Fg5iA(*f2!vr$ zA>Sg5o*X;g5+Xh9bIkUPyc}7!Dd_|1=JXhIEmf}Wxz8u(;q~a|XfK`{;oXMg0|4FU ztyVRHO;5`OhL_o~R-8)YbxISE^8$7}kbxr1kEKtM!zQDcRI%jL2Om~Y8skBVwq5rs zj~*7SDo#`r@C}5Cnr&Ns0{T6-z4AwC33HO_jy%6t9oi;ry*u#gqO%Vzz0i@7la0c z@EeuN2>?{2#eyuHjjv*i+5Fs;tbbfS0TE8x|Hu9t+<$EBSha!3LaiNWn_;Esy^cj~*qA*kpG)KL6MC|9(=L)P9C%TQb@P&3z%Gn(ETEZmnSA)YcVgi zEbL?2EJktMFhc%VZV)&|w4wfEx8THgGqbh#T}b#pgXWnjOX(Y+L$`V$W7?>tUB^tX$i=kD-jA+5xTAfTA`^Erh4+fVwRCWFlS~PMzqbu{w|10y-)CFbF(CiYz7~Hk z8uPd_;K%;%@_`UN}@nYX0ioh_unNL#;hk)niIDq z*+RqljndtE%z#>S&pr*)8g&P&wD}*wcK;!`|6h5oAP|Atex@V^6gzaG0ND3{V>tpT zNQ$H0-HLHVLeFLgaJC+~rId%lyhHBIZrgq2KS%3gE@pbZiyi0@!;%lZQ43>}eGoNf zgP9~OUXr0RNp}y)N2oL8TV}!09>^ECp2%s-nnyhfy>~8f|qVxeH$?5ZKLjmsaE|d&@`s$a80#%^PoTvW- zIc~NfInT0JxDOj={IsU#^<%Zmo=as6mrdjT>q_X{bovbCY>g*LFUV+gvsHX7$6I`= z^}cFEe6Q?T{j=I-N1(JPBH8wEFc{ZcsG2VdsvAH0%k*#+8fA1J=val&uRU%TgdLS~ z44i&%-*RlQY;!hZ_Qn?B!~PTU^)UJ=Z+H=6{Cny_D^Qp4n(jJ1&TifF3KH2Krgr8B zAF4@VRv@;hL|40GLnJ#lN;|}0>+roCzpZWR;miSj;44yZPLVhfw-2Voy;bs*DbEIO z#-)~hZgaogbHAnc6Q)Oh?zlRbP65cJhJ%aD#WsjG{p@{%uhgymFG@D>*=`m7Q2YVj zPPP&|<<*j(HzQNZFer;^1Q|XfRc5+>iPt9aaGbC6}FnPQ4 zE}?fhNGJH6)aMUj~heX+Ac;)5uqq~7;Z;>X;? zvCmX6d8=(kWmM)Y#UQqN;G|Q!>q!!6bz2Ti@__K$G@bv6T|Wl|uQQ_h(>n(r6m$~0Ys8uWz-o8kO;Eg0677+gE+@ffD-dT91-HQ!6a`}Nd#1X3Ix z(TklDcz>CiqIY$@Zy}CFxxxC7mg+wL2vp@xpU>Plz{p1B9WRTPqR1`#CT2g5uJ#=# z{i}dKFTgnM45hm_u|1|AYN2;jYK{Ec@UnaF2Dj=2< zJ_R61t^w7&!BS|zWG1S`Rx)ySGx0Ce)8xZ(b!!)|N&N&U^@=XJqHq0pMTeG$Tl>qD zwo6<;*~Pe_*^10g*ZW5RwW}C)*k%2f>Eq~Mremb`U#1+Me04|BpP%u`5DI)x(cV0R z;7pNb{Ohv^#P#`EVoa*-39@?xhRW84x~6hrRaG%bp{AqQP2{{qn$=y)F|AjRJ0iuV z?|zqk+%Hr8=2mR zG<7+VMbW{6>|ob1oY}A%T4mh`Qjnd3whi`GS)*1IA>FBtWJu?HJCp^-g_QVJ14Hbt zRpePh2t=1#3m2}OI6cr*Kr{Ksd)&}O6QPf(Z+60t1yPd6f^S)NJ>XvNr&P>8Da{8O~3r(dc`r((_=qYQcMI*L# zjXc%!EINs_pBVrp`h;X7@NGn{hz=G!I~6o1(30idW`2FycvuNRu>W*C4E~h*U|{*Z zQpb;btM&KJ1Lvzo#y@L3&c*T2gQ*@3=oo?aEL8h>5{pUQx61^Va^BC~9F6je-u&jG zxy369Dgs|B7R?reAj=KT`!rBNL99{WA+gt?*$gl}mtnqn3|dfvm_zA{n|6+)bIE zMZN#a1QS0shi?}M^O!_r8-%u*rAy=koWs)(o&E2hovJ*~_m~;Af_}&IVgD<+hzx#% zvJXB*(-<~OGzeck@4hayb2H#f2lf--DSEHwZZl0ur@oE)6QggHxe|;4R3`09$!Eln zS-HL2!}RzgO`wX}W3B}K%Vc{6wNfHR#!^LLoYW5#-oykXOL2k$?bmpZw3)#6762;p zY}+z22a3A2+j3Eh8QBL(xHV^Li0BBoVggJq13L{)GT4D~eH!)^Rj}Lz=qaFHp_rWn zSn0yE@W>Dea`MJPAYh4tzBu!9<=U`xv8SuB57Ud+c4*Jo($K>_b*IHXe%lmipcgPb zbf@3G?6a_d-oWF;Wa$#YEfm3afveJZUgUW)JWfe!7Im6{joR3|VZch&zcZKq>%*s{ zroj%u&bJ2llUusQCOSdysNa5~W^;`bw87*|i|A!WMjKSz&_Y8@#?HJ1-%rOUL}?@( zPBmF{iH1f3`89^c-yP$f=ky|2IM&5o(J^=Ju4$jh*G z4lw;uV2)H_h7s!CRl_3$IQImuKnkknf=`lUYh~9w8YPf<4i?(CJWecoI|?JHI#DKe z>&`=kLE8|h{&eC7_E|BlOO8HHH@DqC^Z~0bQA8rau2=%-dqXFdIii5lpDO#la5dGS zl4~=aYhSSL`Gla#XVyG(0HMX`#mOCKgxp^NzTc2L9rm-tvs4QMU)g+>+|${vBBK99 zosOBv%KY~I`;YnJlQ^L-SzHa$E1i-S9U6?(vFW`nH{7cv$Gb#lMSQ(vw?xD^|24`v z3V1z?_7vu+G1gs7{qVH$vSX5^q}s-#-Ahr0WV!6$=KJpkz$@tnj5w=9{isIa^Qs_? zFU|4+s6&DFXH71uxwHpfv`G{&@5Ipsr~wpnt6xZ%Wmk{W zBmD&Sufzc=HC@}gVuPm}0Gy1yUs_>9i;!>UtPD4;fAlaQRB z5?6Fwxum#|mb)$V^k(d1eapwIZ9CY3hfgJL7qZX6S~EPfZSvM@Qnu`NGYhXr6t*E6 zt`>91i8|F{c4K?}5ca_knl3Pea~OT#y_FKwpWkbXVxV1(Emnj7xDy>#D-%izlTv3m zHr4h^KOuaa1<1v?Yw`OAmBEdS%UX=l1KC5g17OC=Itbjcy`e5zW^MZuqODtUf!0Pc zUtgwNogZIsqbX|78n9;hN;mWG!Ou0-)on*7bW|O?zRz5DPh06j3aFbN&p@Ra3*)Pf z-LwuPnh0(``%iE#?FLC8R>RpT~ZxFR#jp_96 z2#8p}DyU0?B9;7K_K%wjRjg{)WTYSNMMadLWv8byy`GcOt@s z=L2G55B9>zwH@t<)<34z#%6>Z1mk6pZ`9Rq48=%b(ktovdpm1?VCDz7LESm@+Sb~i z0h|(uBB@8sp*;;5o&k|}e-iAwUf29nT@Ov_snB#|Px5c$I$)*V#xZ}1Qh0*8XA9l_k0$9(kGUr&21<_ttq%u)T(*-C5x)&*qHK}m0!{k z_w2&`W3&%iqZXY0vpep;e_l4RQWyBPf2_93y1dE3Ia(_9a;HM$bC28+ifKvas{}W9 zg9mPT|Jw8Beg1C%u>Z{;|JvX-`CECL@N-OyBvOAlTiiN2Og#Ej?5MU}rEs^VpZS-l zs=}4J^qQ`TbvK8mY-hOG%tOKF7ZYDUe0?X{I{TjKZyg=6jR}sTFJ+LGI)Ae|x%*aq zGR4*~*Ip~3**>0gki|L;6F#*w1GT#6BA3h?6LHA`=phuP*0nLG$>%)MG?gqJ((b)|8%cOhzGn?H1HI_~dft}&N z+oSONGOPF_OYwibeFIRkh~A)>18)Zkf4ah0xzB8dR7L3!wqai)uXVdHkC%9Uy1+cz z=tS3`HW5fBiGI!Cn?6o1eQt8jxBO}B429h0yVSJ^d?^2J0^d22=S{=5J$%sZ53Y!PACxm!c z#Q=GV*NA!jRh>}YCi^_A z+q&*7E4328+AC!4=W$V;qy5#%XMUGfHnudQ0W)o(6*U5T9M?ujD)ua%k~TU{8rpDf)hA2r`u60rQPJ%elF{qNZ!M0``0-x?0@ATa8ERw$x;UsP za>Ff(r7uk1J3h~heIU1|V(5-^3jX|4crKbKzFG0Ub%^oI1=NpPjd^j)+4w?`TtV*OfwoPlc z`pMR9dD<7R_&+rgSJTVxfA?)6=h11o3ck3Ua#N~eu_lZK3RB=u43bs}bgmnpl+cM! zK2-g9p6`TA`Ef?T9Q18q7P{(mioZf_E{lcbltj16lhj1#g^SM}fmCG87hkNAPjNO6 zJNd8ShJ2kiqS&rHxzQdas8;>6O!of#jW^u07tKbtGnG~!X=E%VJs*KMiwt{xNYEc} zvUkJM-!!ji7nnsa(~1-PG%uiHvvk(hg%clE8N znkA8~%yCrZ8eQ#c<@+s3uHOwUCsX}QKc(Dc)nanMA|H{JPl5d)a@U==L>*!7}NCs(88Zj#HK)yo9%i@y>a*Z&2U{mX!f{xwa z82^^aFW5v)CS%(hBHnX?N{??tyzPqv&hw`E69%t*cIR5=+CsP1)1aXZ@zph{Mq|=o zwqBpNT5i4&qL`!FU~SkVW+I2N1vR6Hnqs3cwhhwdyD~QC!1y`rHpcZxDJ!?QGZ4+# z8>HKjLz*$j$4#R?#|m3!!!@^f$NbGaswk3srfNRZ?Qb6k4hJ8!YQFPSA8Svh@v)hU%$HgpBi0^Ig^ zGd9ex%A$jEmOv5-MZ`Tv|zY3N+GI#xz z8$MRb`k+Jdf|b)z7^W&0$>1IPI5qK=_aE<_*5C_hsg*c22>8P0&4yHx&{*x49)baz zZM9#|V!MuK*i^tHzCJUmeeonBRkx8Wf)(p>E0W77n9?2^BeSJ408@w0aqt>Gg{!QQ z<#lp>dSWQ!K&hFiU4H$be923@KU@JgQ-V^7F0HBYs0*cw>A=R0d9MmwmZm|R(u3Yt z)3oMe&RTxLFr8X6iD31c3doQ?_k zduWTYdI0xRyYNnu;Q8v@3n>Xi^@dT{s7tef?){_+OJ8~Y3)?=}>$O#}ahipixw|6G z6wVyVsX11B2_oOFzVU1Q8|gmv&lU@g4(;(LKcBr3&^7#IDSajeC@MJs`q z!rC}$Gt3*dLw-GJ`OPHsW-Z#>2D_RHSoD}fS#0<)v_lh$>3z?A>D(WSatI7Wbsp>J<5}c_ne77aAfe}$!l%z^tNe#DT|ERAe zreZ4G9G;uQS z>Yd$cmHRp)bj5l6Otc)NUYy;7>+%2}6UWj3sI|d2ILiM+U~jr?%xL<7k>7ZlqRNlm zyGvYg&YF{exY)`IA5e#HT1TgNHdsx6@)J0>$6Y| z_m)9%hhgFzx&}TEF<_axCwp+9!@g&3U`uZ(t|pVwCyn&}i(E#_?9}fz1&v zgI~J

kkGi9)z@KZh(ez@7+JovSOpnfWHubH#j@Ej$qa-q#JLR{;x<*!|pTBGJ|- zM!TOaDU@w(j+z6XSlh?*x&+N=;3cPRmRE2*j$b4(vwHNIcakUxsEQc@?AplmC~Pz*lDrI)2dmo+2MKK_Bn z`5T(t^EcC{snQY8Dk-sXPOIttx%t&*I$CF~d7E4e{Gf{)*3~MP$9%MNM`ZLkkA@9u z(BNw2(MFA?uZJPe9~gYb7!?0XzF>Mwo3Xu>zWJ*f;0R7gI0iY5O=fwvn;o?m^_N(u z4E5|ZG#>!5j2G%m4t`HoP#>0BKXKk~HqaQ7gtKeA6}ntU+x#pESvg-l@Bbu0zARW^ zZl&hEFzg6oZjan3pybtclV?y@#D7P@+^oj- z|Kzu>+vJ_wF42&Rb3MSA(k)3ragnxnMsmnlLPxtZtkh|a$M@Vdk@L1s4$n{jPT!N_ zwuP!TId|)7ORpDfCP5mTuA9`?!#8Wkx>Cb`R@AI0L=ifVzLhGLa}><<(alCdTlOu~ z(w2=mi}eMb&crfx-zLzCzsU12zXe$n_K+7*%io1&FQ)GryhpPROJ}kDGwym%Qkr=r zT|H3)=QY6j6Zym9>>Y2q?Uy2?0C_4zh%y>A{lbTKk7&~vJQ@rBwEX*Dq^S#OKKW_) zu9=Gy)_fARXEsD(u#$!e^dhg1*%?y7LdQV2IU-bC{k)Wm1aj1 zr9u2};3l&l_8hH$KjNX-TyHsOV1$*sq$E7<&ikUP-1zXkj#TF&SIa4!7FJ8(hSEbo z15`*CLXqEn8&%o>+@FlG3_qzlMX9kf~})v>gO#Z4uuUj**6#9Z7CeJ6^#*swrJV4MEsTy>rUu z8bfr&*;2ljGb93c-ncKwv0n4u3CD|CTBE_o5BKKV?72{G>m?mHeXU;ekN|+8ithHB zu1>UZk6oR}vNYaL%1g*dl^9!$NuG3b--FbayO+&2v3*7qYEs{L=B;?Q<-x`%KNDnr zT+A`m_B^oQebdtA*=IS7S_oYe*yjbb2I@S!!az?MJq}(QGdLEq+L@MPXsJ{HBG@0O zUBi5H7GCl5tq6nC<<9?$M(rm(&j#KtnOT90tB zhRKpu>JfNL=osQn3fW!Rsv zh7HObk6>`E^325XmwFaC2XbaTOnN(SUcdd%I_tT|Cq()Br7Rf#M{VaB)Z`kjYZMg~ z1q(%*sDK~{Sm<@>5)cuPjvzz@r3R#zK%yuejDmpFC{=0%385y`AR-_&5(ps)y@nP7 zDV}ewne*qIea_zd$NrU>B$H&m$@jkR^E~%`-SV{J&(lQv-=0^_z02)ChDWO>={ES=xWNQeKha%;OF~R@*4DN>47-yh~3BkQ9Fi&67N8zp&fb zl{@78nsRRU_bt(f28|xELHDDsBeH!3t>C~q%fh2Gl(30zI?t(^C6yl>c6=P5$D}Bp zy91&CrMEwoDHfr#(N_$hw@PgBNN=PvYtKCU1oU~=X0P`nuz1<)?#q~lYQWypwH~8? zPUj{Clq?Fvd93JM4=Xit0h7(E|1KWj`eAK%G5WSnXE)D6vm%YSSHDW%K&jQ@e}er`vwK3|N1Z!`XBK zM7GoVEkUrClaG8OhtaI-83vKV#St&YIEOfG*yBNPUL!A__%&3dizKeA*u&bBXtg*y@PS-28NIitqw+_Nm11oDTjQ?6Ie z)3v_}OG3E(l9eiDhZL2wjf-}JHZNWO&;TC$!{y%sU_B`Wg;j~aDbCf*zc@Y4GK^>n z)mOh$IW}wc^xM%3v}Gew7sGdBYkL8OyCM6NGd?6*td-gw zP<6nzDdxb@*Sg$$F=yeAMpb{X*=I<@82}Z*AEr7!87(8T7Bo@ZJXyCnS_ui4%n7tx z*y)cg3FW89FfunbhvZ;iGulowkj78BKxog?d)HsWnuo3eVl~)Q1NP;4-I1X6ml$Cn zl~X@X?>tSoOT(v11|LZDJZd=)aIQaQU71VnOcHo}VW;iFfiyQS)peP&rCfs7ESNQ# zhEg^i8+g7woMy7o$O>AgDGE-oyz4id@Lj%VO{mE_9#BQEI<2&_&g#S2+|u$X^*UayO(S3XqiYgxJV`tSwTkJe2Qa~_Fs>*3uY zU*u1#X5VM~Fwj^@o(0H><;?yVaV3(LXCA)~Zkca`WO8c&7GouW%H_j7qLBH4Ckr** zJ-RlAUY#&ECNJ0sC;XlmAvXfOW_UF-20(i`bx8As!wS!O7SZ(9_`0o9oxShmVN8w>Xw|BfEyh7+b z_*#$e$F#)(oq&g1kja9&3|)n3<(1vi2>-^>sv}K>p>`b(m%1)jR(Yvk(x^z&L1WU8%T*<2Pkh-(;w#61H zTBROE5^&%5URgXmJEOL#;(Fxx8c?1YzKB%7vcF&i7xfQubVAG9v?RJ_r_zdy54Nd& zTG;vu2`$u4kT2cK*|xyxp>PrZ$Ew<*cvq5USKi*CfVmH6M%myHT5s!yeE)DA03`0t zCC9UEE;q_ZcJ;E}?~NKnt~0Lz>Gd)U;>I{fFf;{kl0nYHf+Jn{qshZ$6Ub3&l(E$2 z_yf%gMoHDMC-BAGNX93j1dCftS*W}Kw1^M&$KbhG;jYq8}M9fDI0+SjkwTA7Mf6v|z=uyFLy zmj?>!^k+0^K@b%N2c3!lgULi5ic#!&S9?R z?=wBTYCNUn(tqpeTLH}hgB5#;unc8w0)C4qv)iE5!W8xI!PvBL9MxFc#@nltjBcah z3A@pX3Y$)Fkk((~Nzqr^fR;`e^EOL7by~738Ps-Wsl2kMMEV52)aNmuYqa!*%X2t& zs^+{1wJ2OKlL?LhZP+D7T)bRDv9D6q)wk@cD^p;L7cWrG`VaaJv@h zh_mztKhAstc8{TmT=ws!?d(*lfmW&oJcZR3hMpQc*n7Om-vV^zpE!Kc)bexe0lK|~ z96Nw|;0H&2peOk*mNVBGiXp#PyZKlvWgT!8BZPXWQH3M~<@M0#sqhuMk03KicZ#(I zUBqw;W8-IE9+L<8Y$Q>djmT_x4ChFw0(HF$%iUJ8Y)6ocGki@pS-efW<#OoT0eall zyKdJ6!MD)$0`b|OmHl+|Kw!#hxfc?OYexE@O?sb`^3o%uI&Mz!QYcS`&W(|0(r1jWO3Bq+zw@|)ahyv!317zYWOP$Nzjr&Zd>BcV zViu4p>`9v~ZmRIy_}!t3D+$tD?IKwFe(=_ic*XtcU?IDL1=5E%^E0o8Cam?3;npo< zmMEd#1*bdaP+e7Y8_B?%&#F12fjIGDYy8$1fJ}4{6oj9Ct2tr}vF3E7VQE5`U|6Ew z9Y!KB)I`YTFHVL`n!NLZW4e$a|#K(_7p7xp474_gY@0V2C-RBA>%)??rmLZTKF z2Y0I^EMy(Pt2~iNx(0jRCju_#>pG-N6rY(soo5yTCQNqz?`Sdu0>PeXP^UFa{vYKNN5M8R!b7I@9!`E<~sNVcYsHu z=U#r%XkBydEPi;(Ia^RUSSj+1`PLsU<$Bge`9440jXsB9qv+TbSgjGA^cBoBdS4%w z=L|qQp_9}Q5uDd5=tv)Jd0KxpcO&2~rg4}htJa$cJHG4?{jQ;Q5Gg}gnA3S;6Y}m{ zl6=%LuB=`LUhKE#5M?AeG+Vv`uerRA~Rf+>)L@bF)QL$_A{dtZtWO)X6gcF#Uq^AX;x;DO_$(m)R+V`+!eo?lVZgU4G`5XU zltgf@8ml(Y@lZ*CORfP!7@^fkx3vU}>-79dOAFW)e~vsb!wr|=55LDBJS(__WN1qY z41x;sPHxO60IZ|#RyydL{#zaO0;fe$9gm6#-KIKp?1PqOgP)tFSvTj`2d;`3MUHDo z=3!(Y<2puVm?BSalu*-$Ny5jU(ECmO+k~)3YKHvxtq@HX9e9pJhCN!}&x_a_eSjXOxE&S^ z4bZpl!Oo=N{;?u0Uehrm`*xVRQF#ET;xYGw6HKcI%Co4M_Hz~%qDCPBzvQI=TxP3m z0-c`#p+EVB1zVXn{{E5%lffC;Tu^Qg^%*SJN6ir>!@L>8yU(pVRCmHHbzf@@hV_L6oA{My}>`# zKWXy#aeRJ|?YT^Du8=BCAGX5G(m&3Mx0v&xH|bB0ZhK8L*md;dC>z5zFTsE^a18-PRQVC_6Wo9z}rpz_zbqoX+k z1hvbl&8_#%!}j5+zV}0xA0#s|oQ=~I%de5BtfA-Aoog2pn5933o0OCyoti^h@c@$qvpyM}{O4b@?6+dIC=pIQ)3(#lM z$p|SjHVRh9vnpI{zvxF~xZR^Y+nFA6{78^>G$u^K*CJ-GvnTCvSP_hV=rDDv)CEF4 zd3$QbV`0PI(P6v;ELeEIbaHzPb$#AD0E1 zh)(}9$^cZzf3BSQ4-_&0Se09UrY=Gcpq9k~Ac~=n3*2$}DAp68;tWRr;Q|5rnFU97!4|qc@pb=4>2$su0~`~#PD$puAb$3hl=^=&-|e!xCWs)SIdDyh zs-4MFbw`fa=j3q-)MZt!Mgtu}e1;IsNvVetV{4OU^qh+vJ61O605LArlr3k6Fu%{h z<&$d-dLy55@aFZ^QTuaZ3Q;G<27mO57Y1JaG)I!H`OL$cwTrMfx6`R2lQFymk>rQ zW-Wtv)*H-n#OC|}36x?Ks+J_|)u>xq_jL>bVWgf}(>Y<8-S>dAgaH@0G zp~d$6+99u>cERt*iHSGG(_K=`E4_NC5>T+tZ{sRYOZ&8Ox^2_DF7&XEAKbG$KW?=2 z(1`TtF8)QDhS}txSy#61y>;`4V`EER&+3ypuEpGrx;tR)ecpQiygW+4TGO~N`*)Rf zwujHdZzJm}qxC`=0ffZ3q?<=wUALrkExv|XErgaWtcsNv2tLz1QF2Jq;og<4Uen-- z@0vCfb+av2c9^lX;wtsrG`u!KRZjikR$x zB*?#4)t*?Y8-j;fsT*b+Ym_SjmsS3!R_-h+tuIGCpvxnmcEBg)+FPd*g@#)lk2h}) zm0LW-S;E^_+$lu`1}*Bgm_eTt`;n_%Nh?Js(uZojt#?E$HRz?=h_haz2OAf&Q1qM{ z@%q|^(ASZ{@3Z>yYm+^{qOWG5GtU&6h0G;{Y$|snJzp%jS(+NIstReUdubmt-XZ01 zY8%mUs-}_DNGr?ck-Y0~yc(t~KkWcYorl{GmaY9>_ysTznC^2(BF za$F?ze}2L({;|@hFxhyg{hflEah`{>u|(nfp69`u(s_jlhi3vswuM7Ug#<^huT-v>XQZt#B`6}mZ ziX`IW*`7YD{3`o|p^y&~dsy@D9|wyB-Mf_EF`lPPS*Odo&-~rsbnKAi11}@%4O>*z z?RQHKEu7EC#VbPWl;ulz4#amo8+=gKfBl7JaHN)?{EH7C3RB(j!z>r%q8iM#dTd8Q z!5QY~8zSv70Qw+q6RGYzvh5hJq{22_YY)>11DCjuH*H!+c}5Gu>?;HJrm{V^w`G41 z;fW6G#hjuR6I;w!RdWC5FhAG}!xQ)$wQ%_T>dPs;)-A)9WJP5qMN7&}f!-4pQP#RP zU&GFKCzKnwt;+Tdgg|E@8vdJ(W^J~BS9bLlIDLsunFYTy;>xo1JHwaIpf5)kv`nzb z$*;&w_;*dHG_h_OQ_5Q6tU;F3xkRd-d5FKfcEnirm2GO5)=0P;-E(Poxx_`%)iL9= zizYL(d+}kQ4Gv|Gmg_0r#rsRhgXQXhf8Fmv;G_w+<&30Saz>gtgYUqQfm;2X*5rES zoIH)?p>n07;B@|{Xe-6k`n1Y7vAu5G2NLEFQCsZtQ%lvG5iC8b^>kgrA_Ke|s4BH# zhT=;P{JjI&9*%4K=9qDcI{e&k-%6h>HHxhz0pHNV84M!eVeT;144+Bi{<{<1&N;S0 zg1)C861Kv_Y9fWpnco?JW*gT@vl#2Wml237ZD#Z!ZLAh15 zgl=?CU1z6|Da0Lup=Z(RPJdC5OdqimZFfVwc~~>VWyyGEM8HBGB_nvuxV8~=tL#Nm z&WH`k`Llt5t>LTx=EUH@f8qT{;ITozEBvQCvP#nC*$GPu2!&uGQ(SFwnyP{GbKdb{ zx_XNGt{PWGJgK3L@E$>l)V0ud`UcZv(C#kD)U7B!<79iae*SY($o>yjEvwKB78r)< z^jAQSPS9E~%iorO2!t-0+p7q5ssv9f_Q+M1LJ6bbZsRc06%6(2Mcgo$5Q}$BjQe$y zvv8pPt9Fd$cbfX@vrq2WgMpxEXL-gGFRDKOxbq|D{;j1UhhC|sbi?=pc;MCOxJi-M zR~{RwZbb^9Nj;JM%Nr_l;YxkjY+KkXU?O_%50{6w@A(M5L5^godRrZqUx%k;QgzG2 zb{7dzB@P}6`)0OO!8kO1nvtW#e+D`#+Bcu9C&Vc4*!N!$#lGBDP!!^Uz-whl??fK{ z_BJiQ)L@x2eRj((&caUQp0-{oYsdr2*UnVZ6-EEpW$ox?jbT03-w-h9Fp3pZYmuib z!=M(I6QOUdupiRldk`59# zLw=Zye?@KL`~qT^)(bK9mVCf{P<}^-<{wC zq+*!>;NNYtE^vxRd;i4K=Hm&bPAdBZU@yh?4x1)mO6e2(tjM6rUF;doSQa8)|M^KV zGWSBS*tp4=NiW=b0MVgKI^}Plr@z(ScnI@^F>^IbLV2m!b|mitMqI1AwKp=Pb6>t# z^-F|_pDX%?RJyVBD=B$l6M8sJXD2a4N zl_curamXRP@>=@c)ejYZ(KdGs4yC>!Xl#Bl_mqe)%Y#bt)bG$*}@vaUIG_dAb-hTwYx)wLCV zr~WOD@OtO^zT6bhK4_c813;;&^Aw6>=}eZ#d5^-U>(WUNGvj-Jy(;T%f=+9MWte{f zRohSzB82+5Y#G zp9NzeoWGNDe0|DD3MG35Mjef4I!~NRpVf~oPFIlNKw6Uz`>mMI3%?u}4P+Rc-UJkA zUOgNQz(*mAP^sp$afTMNm23WRAvdwpuqAfO^1Q>9(jM>}mL}u2TcCHB*jtV!L!jQ< zq?eMwFEfmAAJT!bli6r?9$t~1OGkd&%k#cr!1?AvJ^viK*nb@t;2aPcK<}MMOm5N{ z7d36@zhR8?JTC8uRoqulHX&`P1xfE!ab!c5k$c}3%nw3;Ngn@aop?I`fN>UF9{TO} zTZzVz^D+(JOyct|rg$h0-6*O#pu-|=SupJAxDBC<4Hw)L(4TWs?r$NKS}=t3VwLWK{g6= zd^}W*`5-i@v1_i$uWa&m9GTuWmme388|siQy)a0XB=}n4uX0XF2%GuxFOm4XEyu># zKFvVp;rFT}=_}R2{HceHAK%i!Ct8@4WmvDZc;jb&s}u!}Es>NLCC!%+DD(CKq0m<> zn<~zLmf2_O6*wX&6mzj5p`9{cnzqsozJQi7kr>yM)vQvUo?lgz?QrxJa(4h`6C_~< ze;41gkqG6h$zI35i^PF&F8JuUaqI5d0}*XUENPX!&@%@NAD|cZjB}Dvl-0E=G{S_@oL| z(9y-bD>U=OnjmxD+GH=Ti<$3Qm(F#egO41TRt`E8nk-O}-dkY1=r)V~D|~5piXFH$ zxt}sX$I%ecdSZ;_w)4js`W|E_$u2GLbju&AJw9vcZnhxR*_2DJ23DImwFBvz+Vy)r z33AInclpwdhHMe2*l3mw4d7tuNia%i`FM)NddzB9dS-=D-mo5@U z!-= z4N#YVPF|vZr=u`V@WKk1w=dAHC17y~#+?u#eethfFrfPMp!W8aInEDkhB31-)64L1 zGLX^qmPGr+GN9;MG5MJxOqbgYgAqs+8}VZ+K~h~?GFUBg^2GcXq+EW%%4XmPTWeoJfXRo(*^`PWjgCw1MTEK@x75Ct!bLpAoBWRl;1^cGl|lzOCpKcckO2P`hds8+vUx&@zm%n@y3?Ro zJe`_a^>a(eohn8*ARrIVoSd7E&yvI(aVHg*le%?G8(L` z{ESch`%7r>KB#tL(xLHEGau9L!pfm*7EwHhTqTzI3BzrY{IerF@MzLuZykQBLcMF0 zMDV;b!b<4&tyuR9?*%Qsn(_O5l)Av`HaMN|Mo?oRrDl6I5;(E&)UStW>Qw=xQ2Os3 zU;%sq;{y;xuV`zgGRVS>XT-D#&%Th_iH|a8PPzOJNypu6@XOh1nRbIi|d9^X?t-b3a8Roa{Y-Qj+;ZZWy!>aV{IW15i*W;6mFURhE zTSW43MzAMXJY^nwGJ{a=hYq1slW4txa^-j*10+F6H!rN#ns-3@3m_fToWq1{sK+Vm zI8sdQ9*sD;j;Lxk`1&{ryL-6$ul)t=iwS-CMa+RrppBv!E0Et{w*y8^0^OS<|MQs& z;pol*;1;UZJH=7~I>fScpttpoXh8qNh1$bc$u+IqXZMQsTR+6B)pvSFFx<(cMX!|UihsRc=H&Jgqz!){}!qZ@Oa;T)k~=(w0_ zkBuFOyWFrP^WMn;R%>$h8aZQT0-lOgohNZ|uEN^iGMdOFaTwrD1jAfSq!EvqM&m5v zhEC{~n%boA08v+AV4)}^kO|9%Wk30Wa@u94u-=cmg9KF(M_IP!lA)2f?222Z#$P64 z^qzipdww0w?fg>VI+6{l_c|ShA7Bc#W)int zLYxDdvi6=Pd{lNj*RCal&KQ73_fDmz64&P->aTz$E7!3DbAypdNe=F0q}pxZVj~M5 z{q%tj%;@&~J@=}*=IrBxdK#_YyF&W}tQG~wABH<&`)YEE6vdWEeG{yA76t-m{`a5Y L|Mdv_GycB--(Zk^ literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesFilter.jpg b/docs/content/patterns/avd/media/avdAlertRulesFilter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c83cd3a8c0d429afa1726f70da2f01be66d18483 GIT binary patch literal 134858 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 Q4l!_uf&YpDo`aD;0bM?#T>t<8 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesProperties.jpg b/docs/content/patterns/avd/media/avdAlertRulesProperties.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f016b3a1203b2e13681004c47a9bf58033321f40 GIT binary patch literal 69863 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�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 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesProperties2.jpg b/docs/content/patterns/avd/media/avdAlertRulesProperties2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b7d38f963aa4995a10f91474102c1a971d57c56d GIT binary patch literal 329400 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 z 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 literal 0 HcmV?d00001 diff --git a/patterns/avd/avdArm.json b/patterns/avd/avdArm.json new file mode 100644 index 000000000..90729ecfc --- /dev/null +++ b/patterns/avd/avdArm.json @@ -0,0 +1,13913 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "8193403604703280884" + } + }, + "parameters": { + "_ArtifactsLocation": { + "type": "string", + "defaultValue": "https://raw.githubusercontent.com/Azure/avdaccelerator/main/workload/scripts/alerts/", + "metadata": { + "description": "Location of needed scripts to deploy solution." + } + }, + "_ArtifactsLocationSasToken": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "SaS token if needed for script location." + } + }, + "AlertNamePrefix": { + "type": "string", + "defaultValue": "AVD", + "metadata": { + "description": "Alert Name Prefix (Dash will be added after prefix for you.)" + } + }, + "AllResourcesSameRG": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Flag to determine if AVD VMs and AVD resources are all in the same Resource Group." + } + }, + "AutoResolveAlert": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Determine if you would like to set all deployed alerts to auto-resolve." + } + }, + "DistributionGroup": { + "type": "string", + "metadata": { + "description": "The Distribution Group that will receive email alerts for AVD." + } + }, + "Environment": { + "type": "string", + "defaultValue": "t", + "allowedValues": [ + "d", + "p", + "t" + ], + "metadata": { + "description": "The environment is which these resources will be deployed, i.e. Test, Production, Development." + } + }, + "HostPoolInfo": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Array of objects with the Resource ID for colHostPoolName and colVMresGroup for each Host Pool." + } + }, + "HostPools": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Host Pool Resource IDs (array)" + } + }, + "Location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Azure Region for Resources." + } + }, + "LogAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The Resource ID for the Log Analytics Workspace." + } + }, + "ResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource Group to deploy the Alerts Solution in." + } + }, + "AVDResourceGroupId": { + "type": "string", + "defaultValue": "/subscriptions//resourceGroups/", + "metadata": { + "description": "AVD Resource Group ID with ALL resources including VMs" + } + }, + "ResourceGroupStatus": { + "type": "string", + "allowedValues": [ + "New", + "Existing" + ], + "metadata": { + "description": "Flag to determine if a new resource group needs to be created for Alert resources." + } + }, + "StorageAccountResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The Resource IDs for the Azure Files Storage Accounts used for FSLogix profile storage." + } + }, + "time": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "ISO 8601 timestamp used for the deployment names and the Automation runbook schedule." + } + }, + "ANFVolumeResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The Resource IDs for the Azure NetApp Volumes used for FSLogix profile storage." + } + }, + "Tags": { + "type": "object", + "defaultValue": {} + } + }, + "variables": { + "copy": [ + { + "name": "StorAcctRGsAll", + "count": "[length(parameters('StorageAccountResourceIds'))]", + "input": "[split(parameters('StorageAccountResourceIds')[copyIndex('StorAcctRGsAll')], '/')[4]]" + } + ], + "ActionGroupName": "[format('ag-avdmetrics-{0}-{1}', parameters('Environment'), parameters('Location'))]", + "AlertDescriptionHeader": "Automated AVD Alert Deployment Solution (v2.1.5)\n", + "AutomationAccountName": "[format('aa-avdmetrics-{0}-{1}-{2}', parameters('Environment'), parameters('Location'), parameters('AlertNamePrefix'))]", + "CloudEnvironment": "[environment().name]", + "ResourceGroupCreate": "[if(equals(parameters('ResourceGroupStatus'), 'New'), true(), false())]", + "RunbookNameGetStorage": "AvdStorageLogData", + "RunbookNameGetHostPool": "AvdHostPoolLogData", + "RunbookScriptGetStorage": "[format('Get-StorAcctInfov2.ps1{0}', parameters('_ArtifactsLocationSasToken'))]", + "RunbookScriptGetHostPool": "[format('Get-HostPoolInfo.ps1{0}', parameters('_ArtifactsLocationSasToken'))]", + "StorAcctRGs": "[union(variables('StorAcctRGsAll'), createArray())]", + "RoleAssignments": { + "DesktopVirtualizationRead": { + "Name": "Desktop-Virtualization-Reader", + "GUID": "49a72310-ab8d-41df-bbb0-79b649203868" + }, + "StoreAcctContrib": { + "Name": "Storage-Account-Contributor", + "GUID": "17d1049b-9a84-46fb-8f53-869881c3d3ab" + }, + "LogAnalyticsContributor": { + "Name": "LogAnalytics-Contributor", + "GUID": "92aaf0da-9dab-42b6-94a3-d43ce8d16293" + } + }, + "LogAlertsHostPool": [ + { + "name": "[format('{0}-HP-Cap-85Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-Capacity 85 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution for xHostPoolNamex.\n-->Last Number in the string is the Percentage Remaining for the Host Pool\nOutput is:\nHostPoolName|ResourceGroup|Type|MaxSessionLimit|NumberHosts|TotalUsers|DisconnectedUser|ActiveUsers|SessionsAvailable|HostPoolPercentageLoad', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT30M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " AzureDiagnostics \r\n | where Category has \"JobStreams\" and StreamType_s == \"Output\" and RunbookName_s == \"AvdHostPoolLogData\"\r\n | sort by TimeGenerated\r\n | where TimeGenerated > now() - 5m\r\n | extend HostPoolName=tostring(split(ResultDescription, '|')[0])\r\n | extend ResourceGroup=tostring(split(ResultDescription, '|')[1])\r\n | extend Type=tostring(split(ResultDescription, '|')[2])\r\n | extend MaxSessionLimit=toint(split(ResultDescription, '|')[3])\r\n | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4])\r\n | extend UserSessionsTotal=toint(split(ResultDescription, '|')[5])\r\n | extend UserSessionsDisconnected=toint(split(ResultDescription, '|')[6])\r\n | extend UserSessionsActive=toint(split(ResultDescription, '|')[7])\r\n | extend UserSessionsAvailable=toint(split(ResultDescription, '|')[8])\r\n | extend HostPoolPercentLoad=toint(split(ResultDescription, '|')[9])\r\n | extend HPResourceId=tostring(split(ResultDescription, '|')[13])\r\n | extend ResourceId=tostring(HPResourceId)\r\n | where HostPoolPercentLoad >= 85 and HostPoolPercentLoad < 95\r\n | where HostPoolName =~ 'xHostPoolNamex'\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "HostPoolName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsTotal", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsDisconnected", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsActive", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsAvailable", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPoolPercentLoad", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-Cap-50Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-Capacity 50 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution for xHostPoolNamex.\n-->Last Number in the string is the Percentage Remaining for the Host Pool\nOutput is:\nHostPoolName|ResourceGroup|Type|MaxSessionLimit|NumberHosts|TotalUsers|DisconnectedUser|ActiveUsers|SessionsAvailable|HostPoolPercentageLoad', variables('AlertDescriptionHeader'))]", + "severity": 3, + "evaluationFrequency": "PT5M", + "windowSize": "PT30M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " AzureDiagnostics \r\n | where Category has \"JobStreams\" and StreamType_s == \"Output\" and RunbookName_s == \"AvdHostPoolLogData\"\r\n | sort by TimeGenerated\r\n | where TimeGenerated > now() - 5m\r\n | extend HostPoolName=tostring(split(ResultDescription, '|')[0])\r\n | extend ResourceGroup=tostring(split(ResultDescription, '|')[1])\r\n | extend Type=tostring(split(ResultDescription, '|')[2])\r\n | extend MaxSessionLimit=toint(split(ResultDescription, '|')[3])\r\n | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4])\r\n | extend UserSessionsTotal=toint(split(ResultDescription, '|')[5])\r\n | extend UserSessionsDisconnected=toint(split(ResultDescription, '|')[6])\r\n | extend UserSessionsActive=toint(split(ResultDescription, '|')[7])\r\n | extend UserSessionsAvailable=toint(split(ResultDescription, '|')[8])\r\n | extend HostPoolPercentLoad=toint(split(ResultDescription, '|')[9])\r\n | extend HPResourceId=tostring(split(ResultDescription, '|')[13])\r\n | extend ResourceId=tostring(HPResourceId)\r\n | where HostPoolPercentLoad >= 50 and HostPoolPercentLoad < 85\r\n | where HostPoolName =~ 'xHostPoolNamex' \r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "HostPoolName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsTotal", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsDisconnected", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsActive", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsAvailable", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPoolPercentLoad", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-Cap-95Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-Capacity 95 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution for xHostPoolNamex.\n-->Last Number in the string is the Percentage Remaining for the Host Pool\nOutput is:\nHostPoolName|ResourceGroup|Type|MaxSessionLimit|NumberHosts|TotalUsers|DisconnectedUser|ActiveUsers|SessionsAvailable|HostPoolPercentageLoad', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT30M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " AzureDiagnostics \r\n | where Category has \"JobStreams\" and StreamType_s == \"Output\" and RunbookName_s == \"AvdHostPoolLogData\"\r\n | sort by TimeGenerated\r\n | where TimeGenerated > now() - 5m\r\n | extend HostPoolName=tostring(split(ResultDescription, '|')[0])\r\n | extend ResourceGroup=tostring(split(ResultDescription, '|')[1])\r\n | extend Type=tostring(split(ResultDescription, '|')[2])\r\n | extend MaxSessionLimit=toint(split(ResultDescription, '|')[3])\r\n | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4])\r\n | extend UserSessionsTotal=toint(split(ResultDescription, '|')[5])\r\n | extend UserSessionsDisconnected=toint(split(ResultDescription, '|')[6])\r\n | extend UserSessionsActive=toint(split(ResultDescription, '|')[7])\r\n | extend UserSessionsAvailable=toint(split(ResultDescription, '|')[8])\r\n | extend HostPoolPercentLoad=toint(split(ResultDescription, '|')[9])\r\n | extend HPResourceId=tostring(split(ResultDescription, '|')[13])\r\n | extend ResourceId=tostring(HPResourceId)\r\n | where HostPoolPercentLoad >= 95 \r\n | where HostPoolName =~ 'xHostPoolNamex' \r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "HostPoolName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsTotal", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsDisconnected", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsActive", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserSessionsAvailable", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPoolPercentLoad", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-NoResAvail-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-No Resources Available (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Catastrophic Event! Indicates potential problems with dependencies, diagnose and resolve for xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT15M", + "windowSize": "PT15M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": "WVDConnections \n| where TimeGenerated > ago (15m) \n| where _ResourceId contains \"xHostPoolNamex\" \n| project-away TenantId,SourceSystem \n| summarize arg_max(TimeGenerated, *), StartTime = min(iff(State== 'Started', TimeGenerated , datetime(null) )), ConnectTime = min(iff(State== 'Connected', TimeGenerated , datetime(null) )) by CorrelationId \n| join kind=leftouter (WVDErrors\n |summarize Errors=makelist(pack('Code', Code, 'CodeSymbolic', CodeSymbolic, 'Time', TimeGenerated, 'Message', Message ,'ServiceError', ServiceError, 'Source', Source)) by CorrelationId \n ) on CorrelationId\n| join kind=leftouter (WVDCheckpoints\n | summarize Checkpoints=makelist(pack('Time', TimeGenerated, 'Name', Name, 'Parameters', Parameters, 'Source', Source)) by CorrelationId \n | mv-apply Checkpoints on ( \n order by todatetime(Checkpoints['Time']) asc\n | summarize Checkpoints=makelist(Checkpoints)\n )\n ) on CorrelationId \n| project-away CorrelationId1, CorrelationId2 \n| order by TimeGenerated desc\n| where Errors[0].CodeSymbolic == \"ConnectionFailedNoHealthyRdshAvailable\"\n\n", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "UserName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "SessionHostName", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-DiscUser24Hrs-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-Disconnected User over 24 Hours (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Verify Remote Desktop Policies are applied relating to Session Limits for xHostPoolNamex. This could impact your scaling plan as well.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT1H", + "windowSize": "PT1H", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": "// Session duration \n// Lists users by session duration in the last 24 hours. \n// The \"State\" provides information on the connection stage of an activity.\n// The delta between \"Connected\" and \"Completed\" provides the connection time for a specific connection.\nWVDConnections \n| where TimeGenerated > ago(24h) \n| where State == \"Connected\" \n| where _ResourceId contains \"xHostPoolNamex\" \n| project CorrelationId , UserName, ConnectionType, StartTime=TimeGenerated, SessionHostName\n| join (WVDConnections \n | where State == \"Completed\" \n | project EndTime=TimeGenerated, CorrelationId) \n on CorrelationId \n| project Duration = EndTime - StartTime, ConnectionType, UserName, SessionHostName\n| where Duration >= timespan(24:00:00)\n| sort by Duration desc", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "UserName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "SessionHostName", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-DiscUser72Hrs-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-Disconnected User over 72 Hours (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Verify Remote Desktop Policies are applied relating to Session Limits for xHostPoolNamex. This could impact your scaling plan as well.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT1H", + "windowSize": "PT1H", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": "// Session duration \n// Lists users by session duration in the last 24 hours. \n// The \"State\" provides information on the connection stage of an activity.\n// The delta between \"Connected\" and \"Completed\" provides the connection time for a specific connection.\nWVDConnections \n| where TimeGenerated > ago(24h) \n| where State == \"Connected\" \n| where _ResourceId contains \"xHostPoolNamex\" \n| project CorrelationId , UserName, ConnectionType, StartTime=TimeGenerated, SessionHostName\n| join (WVDConnections \n | where State == \"Completed\" \n | project EndTime=TimeGenerated, CorrelationId) \n on CorrelationId \n| project Duration = EndTime - StartTime, ConnectionType, UserName, SessionHostName\n| where Duration >= timespan(72:00:00)\n| sort by Duration desc", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "UserName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "SessionHostName", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-LocDskFree10Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-Local Disk Free Space 10 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Disk space Moderately Low. \nConsider review of the VM local C drive and determine what is consuming disk space for the VM in xHostPoolNamex. This could be local profiles or temp files that need to be cleaned up or removed.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT15M", + "windowSize": "PT15M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " Perf\r\n | where TimeGenerated > ago(15m)\r\n | where ObjectName == \"LogicalDisk\" and CounterName == \"% Free Space\"\r\n | where InstanceName !contains \"D:\"\r\n | where InstanceName !contains \"_Total\"| where CounterValue <= 10.00\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, CounterValue, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where TimeGenerated > ago(15m)\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName\r\n | project VMresourceGroup, ComputerName, HostPool, _ResourceId\r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-LocDskFree5Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-Local Disk Free Space 5 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Disk space Critically Low. \nConsider review of the VM local C drive and determine what is consuming disk space for the VM in xHostPoolNamex. This could be local profiles or temp files that need to be cleaned up or removed.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT15M", + "windowSize": "PT15M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " Perf\r\n | where TimeGenerated > ago(15m)\r\n | where ObjectName == \"LogicalDisk\" and CounterName == \"% Free Space\"\r\n | where InstanceName !contains \"D:\"\r\n | where InstanceName !contains \"_Total\"| where CounterValue <= 5.00\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, CounterValue, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where TimeGenerated > ago(15m)\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName\r\n | project VMresourceGroup, ComputerName, HostPool, _ResourceId\r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf5PrcntFree-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Less Than 5 Percent Free Space (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profiles Service logged Event ID 33. Expand User''s Virtual Profile Disk and/or clean up user profile data on the VM in xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT5M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Admin\"\r\n | where EventLevelName == \"Warning\"\r\n | where EventID == 34\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf2PrcntFree-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Less Than 2 Percent Free Space (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profiles Service logged Event ID 34. Expand User''s Virtual Profile Disk and/or clean up user profile data on the VM in xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT5M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Admin\"\r\n | where EventLevelName == \"Error\"\r\n | where EventID == 33\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf-NetwrkIssue-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Failed due to Network Issue (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profiles Service logged Event ID 43. Verify network communications between the storage and AVD VM related to xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "P1D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Admin\"\r\n | where EventLevelName == \"Error\"\r\n | where EventID == 43\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf-FailAttVHD-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Disk Failed to Attach (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profiles Service logged an Event ID 52 or 40. Investigate error details for reason regarding xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "P1D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Admin\"\r\n | where EventLevelName == \"Error\"\r\n | where EventID == 52 or EventID == 40\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf-SvcDisabled-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Service Disabled (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profile Service Disabled. Determine why service was disabled and re-enable / start the FSLogix service. Regarding xHostPoolNamex', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "P1D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Admin\"\r\n | where EventLevelName == \"Warning\"\r\n | where EventID == 60\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf-DskCmpFail-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Disk Compaction Failed (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profile Service logged Event ID 62 or 63. The profile Disk was marked for compaction due to additional white space but failed. See error details for additional information regarding xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "P1D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Admin\"\r\n | where EventLevelName == \"Error\"\r\n | where EventID == 62 or EventID == 63\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-FSLgxProf-DskInUse-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-FSLogix Profile Disk Attached to another VM (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}User Profile Service logged an Event ID 51. This indicates that a user attempted to load their profile disk but it was in use or possibly mapped to another VM. Ensure the user is not connected to another host pool or remote app with the same profile. Regarding xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "P1D", + "criteria": { + "allOf": [ + { + "query": " Event\r\n | where EventLog == \"Microsoft-FSLogix-Apps/Operational\"\r\n | where EventLevelName == \"Warning\"\r\n | where EventID == 51\r\n | parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" ResourceGroup \"/providers/microsoft.compute/virtualmachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated\r\n | join kind = leftouter\r\n (\r\n WVDAgentHealthStatus\r\n | where _ResourceId contains \"xHostPoolNamex\"\r\n | parse _ResourceId with \"/subscriptions/\" subscriptionAgentHealth \"/resourcegroups/\" ResourceGroupAgentHealth \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\r\n | parse SessionHostResourceId with \"/subscriptions/\" VMsubscription \"/resourceGroups/\" VMresourceGroup \"/providers/Microsoft.Compute/virtualMachines/\" ComputerName\r\n | extend ComputerName=tolower(ComputerName)\r\n | summarize arg_max(TimeGenerated,*) by ComputerName \r\n | project VMresourceGroup, ComputerName, HostPool \r\n ) on ComputerName\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "ComputerName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "RenderedDescription", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "VMresourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "_ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-HlthChkFailure-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-Health Check Failure (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}VM is available for use but one of the dependent resources is in a failed state for hostpool xHostPoolNamex', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT15M", + "windowSize": "PT15M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": "// HealthChecks of SessionHost \n// Renders a summary of SessionHost health status. \nlet MapToDesc = (idx: long) {\n case(idx == 0, \"DomainJoin\",\n idx == 1, \"DomainTrust\",\n idx == 2, \"FSLogix\",\n idx == 3, \"SxSStack\",\n idx == 4, \"URLCheck\",\n idx == 5, \"GenevaAgent\",\n idx == 6, \"DomainReachable\",\n idx == 7, \"WebRTCRedirector\",\n idx == 8, \"SxSStackEncryption\",\n idx == 9, \"IMDSReachable\",\n idx == 10, \"MSIXPackageStaging\",\n \"InvalidIndex\")\n};\nWVDAgentHealthStatus\n| where TimeGenerated > ago(10m)\n| where Status != 'Available'\n| where AllowNewSessions = True\n| extend CheckFailed = parse_json(SessionHostHealthCheckResult)\n| mv-expand CheckFailed\n| where CheckFailed.AdditionalFailureDetails.ErrorCode != 0\n| extend HealthCheckName = tolong(CheckFailed.HealthCheckName)\n| extend HealthCheckResult = tolong(CheckFailed.HealthCheckResult)\n| extend HealthCheckDesc = MapToDesc(HealthCheckName)\n| where HealthCheckDesc != 'InvalidIndex'\n| where _ResourceId contains \"xHostPoolNamex\"\n| parse _ResourceId with \"/subscriptions/\" subscription \"/resourcegroups/\" HostPoolResourceGroup \"/providers/microsoft.desktopvirtualization/hostpools/\" HostPool\n| parse SessionHostResourceId with \"/subscriptions/\" HostSubscription \"/resourceGroups/\" SessionHostRG \" /providers/Microsoft.Compute/virtualMachines/\" SessionHostName\n", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "SessionHostName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HealthCheckDesc", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "SessionHostRG", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-VM-PersnlAssigndUnhlthy-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-Personal Assigned Health Check Failure (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}VM is assigned to a user but one of the dependent resources is in a failed state for hostpool xHostPoolNamex', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT5M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " // Personal Session Host where Health status is NOT healthy and the VM is assigned\r\n AzureDiagnostics \r\n | where Category has \"JobStreams\"\r\n and StreamType_s == \"Output\"\r\n and RunbookName_s == \"AvdHostPoolLogData\"\r\n | sort by TimeGenerated\r\n | where TimeGenerated > ago(15m)\r\n | extend HostPoolName=tostring(split(ResultDescription, '|')[0])\r\n | extend ResourceGroup=tostring(split(ResultDescription, '|')[1])\r\n | extend Type=tostring(split(ResultDescription, '|')[2])\r\n | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4])\r\n | extend UserSessionsActive=toint(split(ResultDescription, '|')[7])\r\n | extend NumPersonalUnhealthy=toint(split(ResultDescription, '|')[10])\r\n | extend PersonalSessionHost=extract_json(\"$.SessionHost\", tostring(split(ResultDescription, '|')[11]), typeof(string))\r\n | extend PersonalAssignedUser=extract_json(\"$.AssignedUser\", tostring(split(ResultDescription, '|')[11]), typeof(string))\r\n | where HostPoolName =~ 'xHostPoolNamex'\r\n | where Type == 'Personal'\r\n | where NumPersonalUnhealthy > 0 \r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "HostPoolName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "PersonalSessionHost", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "PersonalAssignedUser", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-HP-Usr-ConnctnFailed-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-User-Connection Failed (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}While trying to connect to xHostPoolNamex a user had an error and failed to connect to a VM. There are lots of variables between the end uers and AVD VMs. If this is frequent for the user, determine if their Internet connection is slow or latency is over 150 ms. Regarding xHostPoolNamex.', variables('AlertDescriptionHeader'))]", + "severity": 3, + "evaluationFrequency": "PT15M", + "windowSize": "PT15M", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " // Connection Errors \r\n // List connection checkpoints and errors for each connection attempt, along with detailed information across all users. \r\n //You can also uncomment the where clause to filter to a specific user if you are troubleshooting an issue. \r\n WVDConnections \r\n //| where UserName == \"upn.here@contoso.com\" \r\n | project-away TenantId,SourceSystem \r\n | summarize arg_max(TimeGenerated, *), StartTime = min(iff(State=='Started', TimeGenerated , datetime(null) )), ConnectTime = min(iff(State=='Connected', TimeGenerated , datetime(null) )) by CorrelationId \r\n | join kind=leftouter \r\n (\r\n WVDErrors\r\n |summarize Errors=make_list(pack('Code', Code, 'CodeSymbolic', CodeSymbolic, 'Time', TimeGenerated, 'Message', Message ,'ServiceError', ServiceError, 'Source', Source)) by CorrelationId \r\n ) on CorrelationId\r\n | join kind=leftouter \r\n (\r\n WVDCheckpoints\r\n | summarize Checkpoints=make_list(pack('Time', TimeGenerated, 'Name', Name, 'Parameters', Parameters, 'Source', Source)) by CorrelationId \r\n | mv-apply Checkpoints on\r\n ( \r\n order by todatetime(Checkpoints['Time']) asc\r\n | summarize Checkpoints=make_list(Checkpoints)\r\n )\r\n ) on CorrelationId \r\n | project-away CorrelationId1, CorrelationId2\r\n | order by TimeGenerated desc\r\n | where TimeGenerated > ago(15m)\r\n | extend ResourceGroup=tostring(split(_ResourceId, '/')[4])\r\n | extend HostPool=tostring(split(_ResourceId, '/')[8])\r\n | where HostPool =~ 'xHostPoolNamex'\r\n | extend ErrorShort=tostring(Errors[0].CodeSymbolic)\r\n | extend ErrorMessage=tostring(Errors[0].Message)\r\n | project TimeGenerated, HostPool, ResourceGroup, UserName, ClientOS, ClientVersion, ClientSideIPAddress, ConnectionType, ErrorShort, ErrorMessage \r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "HostPool", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ResourceGroup", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "UserName", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ClientOS", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ClientVersion", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ClientSideIPAddress", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ConnectionType", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ErrorShort", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "ErrorMessage", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + } + ], + "LogAlertsStorage": [ + { + "name": "[format('{0}-StorLowSpaceAzFile-15PrcntRem', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Low Space on Azure File Share-15 Percent Remaining', parameters('AlertNamePrefix'))]", + "description": "[format('{0}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution.\nNOTE: The Runbook will FAIL if Networking for the storage account has anything other than \"Enabled from all networks\"\n-->Last Number in the string is the Percentage Remaining for the Share.\nOutput: ResultsDescription\nStorageType,Subscription,ResourceGroup,StorageAccount,ShareName,Quota,GBUsed,PercentRemaining', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT10M", + "windowSize": "PT1H", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " AzureDiagnostics \r\n | where Category has \"JobStreams\" and StreamType_s == \"Output\" and RunbookName_s == \"AvdStorageLogData\"\r\n | where split(ResultDescription, ',')[1] <> \"\"\r\n // StorageType / Subscription / RG / StorAcct / Share / Quota / GB Used / %Available\r\n | extend StorageType=split(ResultDescription, ',')[0]\r\n | extend Subscription=split(ResultDescription, ',')[1]\r\n | extend ResourceGroup=split(ResultDescription, ',')[2]\r\n | extend StorageAccount=tostring(split(ResultDescription, ',')[3])\r\n | extend Share=tostring(split(ResultDescription, ',')[4])\r\n | extend GBShareQuota=split(ResultDescription, ',')[5]\r\n | extend GBUsed=split(ResultDescription, ',')[6]\r\n | extend PercentAvailable=round(toreal(split(ResultDescription, ',')[7]))\r\n | extend ResourceId=tostring(split(ResultDescription, ',')[8])\r\n | summarize arg_max(TimeGenerated, *) by Share\r\n | where PercentAvailable <= 15.00 \r\n | project TimeGenerated,ResourceId, StorageAccount, Share, PercentAvailable\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "StorageAccount", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "Share", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + }, + { + "name": "[format('{0}-StorLowSpaceAzFile-5PrcntRem', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Low Space on Azure File Share-5 Percent Remaining', parameters('AlertNamePrefix'))]", + "description": "[format('{0}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution.\nNOTE: The Runbook will FAIL if Networking for the storage account has anything other than \"Enabled from all networks\"\n-->Last Number in the string is the Percentage Remaining for the Share.\nOutput: ResultsDescription\nStorageType,Subscription,ResourceGroup,StorageAccount,ShareName,Quota,GBUsed,PercentRemaining', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT10M", + "windowSize": "PT1H", + "overrideQueryTimeRange": "P2D", + "criteria": { + "allOf": [ + { + "query": " AzureDiagnostics \r\n | where Category has \"JobStreams\" and StreamType_s == \"Output\" and RunbookName_s == \"AvdStorageLogData\"\r\n | where split(ResultDescription, ',')[1] <> \"\"\r\n // StorageType / Subscription / RG / StorAcct / Share / Quota / GB Used / %Available\r\n | extend StorageType=split(ResultDescription, ',')[0]\r\n | extend Subscription=split(ResultDescription, ',')[1]\r\n | extend ResourceGroup=split(ResultDescription, ',')[2]\r\n | extend StorageAccount=tostring(split(ResultDescription, ',')[3])\r\n | extend Share=tostring(split(ResultDescription, ',')[4])\r\n | extend GBShareQuota=split(ResultDescription, ',')[5]\r\n | extend GBUsed=split(ResultDescription, ',')[6]\r\n | extend PercentAvailable=round(toreal(split(ResultDescription, ',')[7]))\r\n | extend ResourceId=tostring(split(ResultDescription, ',')[8])\r\n | summarize arg_max(TimeGenerated, *) by Share\r\n | where PercentAvailable <= 5.00 \r\n | project TimeGenerated,ResourceId, StorageAccount, Share, PercentAvailable\r\n ", + "timeAggregation": "Count", + "dimensions": [ + { + "name": "StorageAccount", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "Share", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "resourceIdColumn": "ResourceId", + "operator": "GreaterThanOrEqual", + "threshold": 1, + "failingPeriods": { + "numberOfEvaluationPeriods": 1, + "minFailingPeriodsToAlert": 1 + } + } + ] + } + } + ], + "MetricAlerts": { + "storageAccounts": [ + { + "name": "[format('{0}-StorAcct-Ovr-50msLatncy', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Over 50ms Latency for Storage Acct', parameters('AlertNamePrefix'))]", + "description": "[format('{0}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis alert is specific to the Storage Account itself and does not include network latency.\nFor additional details on troubleshooting see:\n\"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests\"', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 50, + "name": "Metric1", + "metricName": "SuccessServerLatency", + "operator": "GreaterThan", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.Storage/storageAccounts" + }, + { + "name": "[format('{0}-StorAcct-Ovr-100msLatncy', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Over 100ms Latency for Storage Acct', parameters('AlertNamePrefix'))]", + "description": "[format('{0}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis alert is specific to the Storage Account itself and does not include network latency.\nFor additional details on troubleshooting see:\n\"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests\"', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 100, + "name": "Metric1", + "metricName": "SuccessServerLatency", + "operator": "GreaterThan", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.Storage/storageAccounts" + }, + { + "name": "[format('{0}-StorAcct-Ovr-50msLatncyClnt-Stor', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Over 50ms Latency Between Client-Storage', parameters('AlertNamePrefix'))]", + "description": "[format('{0}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis is a total latency from end to end between the Host VM and Storage to include network.\nFor additional details on troubleshooting see:\n\"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests\"', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 50, + "name": "Metric1", + "metricName": "SuccessE2ELatency", + "operator": "GreaterThan", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.Storage/storageAccounts" + }, + { + "name": "[format('{0}-StorAcct-Ovr-100msLatncyClnt-Stor', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Over 100ms Latency Between Client-Storage', parameters('AlertNamePrefix'))]", + "description": "[format('{0}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis is a total latency from end to end between the Host VM and Storage to include network.\nFor additional details on troubleshooting see:\n\"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests\"', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 100, + "name": "Metric1", + "metricName": "SuccessE2ELatency", + "operator": "GreaterThan", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.Storage/storageAccounts" + }, + { + "name": "[format('{0}-StorAzFilesAvailBlw-99-Prcnt', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Azure Files Availability', parameters('AlertNamePrefix'))]", + "description": "[format('{0}\nThis could indicate storage is unavailable for user Profiles or Apps using MSIX App Attach.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT5M", + "criteria": { + "allOf": [ + { + "threshold": 99, + "name": "Metric1", + "metricName": "Availability", + "operator": "LessThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.Storage/storageAccounts" + } + ], + "fileShares": [ + { + "name": "[format('{0}-StorPossThrottlingHighIOPs', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Possible Throttling Due to High IOPs', parameters('AlertNamePrefix'))]", + "description": "[format('{0}\nThis indicates you may be maxing out the allowed IOPs.\nhttps://docs.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#how-to-create-an-alert-if-a-file-share-is-throttled', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 1, + "name": "Metric1", + "metricName": "Transactions", + "dimensions": [ + { + "name": "ResponseType", + "operator": "Include", + "values": [ + "SuccessWithThrottling", + "SuccessWithShareIopsThrottling", + "ClientShareIopsThrottlingError" + ] + }, + { + "name": "FileShare", + "operator": "Include", + "values": [ + "SuccessWithShareEgressThrottling", + "SuccessWithShareIngressThrottling", + "SuccessWithShareIopsThrottling", + "ClientShareEgressThrottlingError", + "ClientShareIngressThrottlingError", + "ClientShareIopsThrottlingError" + ] + } + ], + "operator": "GreaterThanOrEqual", + "timeAggregation": "Total", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.Storage/storageAccounts/fileServices" + } + ], + "anf": [ + { + "name": "[format('{0}-StorLowSpcANF-15-PrcntRem', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Low Space on ANF Share-15 Percent Remaining', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Storage for the follow Azure NetApp volume is Moderately low. Verify sufficient storage is available and expand when/where needed.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT1H", + "windowSize": "PT1H", + "criteria": { + "allOf": [ + { + "threshold": 85, + "name": "Metric1", + "metricNamespace": "microsoft.netapp/netappaccounts/capacitypools/volumes", + "metricName": "VolumeConsumedSizePercentage", + "operator": "GreaterThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes" + }, + { + "name": "[format('{0}-StorLowSpcANF-5-PrcntRem', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-Storage-Low Space on ANF Share-5 Percent Remaining', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Storage for the follow Azure NetApp volume is Critically low. Verify sufficient storage is available and expand when/where needed.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT1H", + "windowSize": "PT1H", + "criteria": { + "allOf": [ + { + "threshold": 95, + "name": "Metric1", + "metricNamespace": "microsoft.netapp/netappaccounts/capacitypools/volumes", + "metricName": "VolumeConsumedSizePercentage", + "operator": "GreaterThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "Microsoft.NetApp/netAppAccounts/capacityPools/volumes" + } + ], + "virtualMachines": [ + { + "name": "[format('{0}-HP-VM-HighCPU-85-Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-High CPU 85 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Potential performance issues for users on the same host due to moderately limited CPU (Avarage over 5 mins.) Investigate session host CPU usage per user and/or CPU requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 85, + "name": "Metric1", + "metricNamespace": "microsoft.compute/virtualmachines", + "metricName": "Percentage CPU", + "operator": "GreaterThan", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "microsoft.compute/virtualmachines" + }, + { + "name": "[format('{0}-HP-VM-HighCPU-95-Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-High CPU 95 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Potential performance issues for users on the same host due to critically limited CPU (Avarage over 5 mins.) Investigate session host CPU usage per user and/or CPU requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 95, + "name": "Metric1", + "metricNamespace": "microsoft.compute/virtualmachines", + "metricName": "Percentage CPU", + "operator": "GreaterThan", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "microsoft.compute/virtualmachines" + }, + { + "name": "[format('{0}-HP-VM-AvailMemLess-2GB-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-Available Memory Less Than 2GB (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Potential performance issues for users on the same host due to moderately low memory. Investigate session host memory usage per user and/or memory requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 2147483648, + "name": "Metric1", + "metricNamespace": "microsoft.compute/virtualmachines", + "metricName": "Available Memory Bytes", + "operator": "LessThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "microsoft.compute/virtualmachines" + }, + { + "name": "[format('{0}-HP-VM-AvailMemLess-1GB-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-Available Memory Less Than 1GB (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}Potential performance issues for users on the same host due to critically low memory. Investigate session host memory usage per user and/or memory requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 1073741824, + "name": "Metric1", + "metricNamespace": "microsoft.compute/virtualmachines", + "metricName": "Available Memory Bytes", + "operator": "LessThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "microsoft.compute/virtualmachines" + }, + { + "name": "[format('{0}-HP-VM-OSDiskBandwidthAvg85-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-OS Disk Bandwidth Average Consumed 85 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}The OS Disk is nearing it''s allowed IO maximum based on the Disk SKU within xHostPoolNamex. Consider review of what applications are possibly causing excessive disk activity and potentially move to a larger or premium disk SKU.', variables('AlertDescriptionHeader'))]", + "severity": 2, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 85, + "name": "Metric1", + "metricNamespace": "microsoft.compute/virtualmachines", + "dimensions": [ + { + "name": "LUN", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "metricName": "OS Disk Bandwidth Consumed Percentage", + "operator": "GreaterThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "microsoft.compute/virtualmachines" + }, + { + "name": "[format('{0}-HP-VM-OSDiskBandwidthAvg95-xHostPoolNamex', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-HostPool-VM-OS Disk Bandwidth Average Consumed 95 Percent (xHostPoolNamex)', parameters('AlertNamePrefix'))]", + "description": "[format('{0}The OS Disk is near it''s allowed IO maximum based on the Disk SKU within xHostPoolNamex. Consider review of what applications are possibly causing excessive disk activity and potentially move to a larger or premium disk SKU.', variables('AlertDescriptionHeader'))]", + "severity": 1, + "evaluationFrequency": "PT5M", + "windowSize": "PT15M", + "criteria": { + "allOf": [ + { + "threshold": 95, + "name": "Metric1", + "metricNamespace": "microsoft.compute/virtualmachines", + "dimensions": [ + { + "name": "LUN", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "metricName": "OS Disk Bandwidth Consumed Percentage", + "operator": "GreaterThanOrEqual", + "timeAggregation": "Average", + "criterionType": "StaticThresholdCriterion" + } + ], + "odata.type": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "targetResourceType": "microsoft.compute/virtualmachines" + } + ] + }, + "LogAlertsSvcHealth": [ + { + "name": "[format('{0}-SerivceHealth-ServiceIssue', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-SerivceHealth-Serivice Issue', parameters('AlertNamePrefix'))]", + "description": "[variables('AlertDescriptionHeader')]", + "anyof": [ + { + "field": "properties.incidentType", + "equals": "Incident" + } + ] + }, + { + "name": "[format('{0}-SerivceHealth-PlannedMaintenance', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-SerivceHealth-Planned Maintenance', parameters('AlertNamePrefix'))]", + "description": "[variables('AlertDescriptionHeader')]", + "anyOf": [ + { + "field": "properties.incidentType", + "equals": "Maintenance" + } + ] + }, + { + "name": "[format('{0}-SerivceHealth-HealthAdvisory', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-SerivceHealth-HealthAdvisory', parameters('AlertNamePrefix'))]", + "description": "[variables('AlertDescriptionHeader')]", + "anyOf": [ + { + "field": "properties.incidentType", + "equals": "Informational" + }, + { + "field": "properties.incidentType", + "equals": "ActionRequired" + } + ] + }, + { + "name": "[format('{0}-SerivceHealth-Security', parameters('AlertNamePrefix'))]", + "displayName": "[format('{0}-SerivceHealth-Security', parameters('AlertNamePrefix'))]", + "description": "[variables('AlertDescriptionHeader')]", + "anyOf": [ + { + "field": "properties.incidentType", + "equals": "Security" + } + ] + } + ], + "varJobScheduleParamsHostPool": { + "CloudEnvironment": "[variables('CloudEnvironment')]", + "SubscriptionId": "[variables('SubscriptionId')]" + }, + "varStorAcctResIDsString": "[parameters('StorageAccountResourceIds')]", + "varJobScheduleParamsAzFiles": { + "CloudEnvironment": "[variables('CloudEnvironment')]", + "StorageAccountResourceIDs": "[string(variables('varStorAcctResIDsString'))]" + }, + "SubscriptionId": "[subscription().subscriptionId]", + "varScheduleName": "AVD_Chk-", + "varTimeZone": "[variables('varTimeZones')[parameters('Location')]]", + "varTimeZones": { + "australiacentral": "AUS Eastern Standard Time", + "australiacentral2": "AUS Eastern Standard Time", + "australiaeast": "AUS Eastern Standard Time", + "australiasoutheast": "AUS Eastern Standard Time", + "brazilsouth": "E. South America Standard Time", + "brazilsoutheast": "E. South America Standard Time", + "canadacentral": "Eastern Standard Time", + "canadaeast": "Eastern Standard Time", + "centralindia": "India Standard Time", + "centralus": "Central Standard Time", + "chinaeast": "China Standard Time", + "chinaeast2": "China Standard Time", + "chinanorth": "China Standard Time", + "chinanorth2": "China Standard Time", + "eastasia": "China Standard Time", + "eastus": "Eastern Standard Time", + "eastus2": "Eastern Standard Time", + "francecentral": "Central Europe Standard Time", + "francesouth": "Central Europe Standard Time", + "germanynorth": "Central Europe Standard Time", + "germanywestcentral": "Central Europe Standard Time", + "japaneast": "Tokyo Standard Time", + "japanwest": "Tokyo Standard Time", + "jioindiacentral": "India Standard Time", + "jioindiawest": "India Standard Time", + "koreacentral": "Korea Standard Time", + "koreasouth": "Korea Standard Time", + "northcentralus": "Central Standard Time", + "northeurope": "GMT Standard Time", + "norwayeast": "Central Europe Standard Time", + "norwaywest": "Central Europe Standard Time", + "southafricanorth": "South Africa Standard Time", + "southafricawest": "South Africa Standard Time", + "southcentralus": "Central Standard Time", + "southindia": "India Standard Time", + "southeastasia": "Singapore Standard Time", + "swedencentral": "Central Europe Standard Time", + "switzerlandnorth": "Central Europe Standard Time", + "switzerlandwest": "Central Europe Standard Time", + "uaecentral": "Arabian Standard Time", + "uaenorth": "Arabian Standard Time", + "uksouth": "GMT Standard Time", + "ukwest": "GMT Standard Time", + "usdodcentral": "Central Standard Time", + "usdodeast": "Eastern Standard Time", + "usgovarizona": "Mountain Standard Time", + "usgoviowa": "Central Standard Time", + "usgovtexas": "Central Standard Time", + "usgovvirginia": "Eastern Standard Time", + "westcentralus": "Mountain Standard Time", + "westeurope": "Central Europe Standard Time", + "westindia": "India Standard Time", + "westus": "Pacific Standard Time", + "westus2": "Pacific Standard Time", + "westus3": "Mountain Standard Time" + } + }, + "resources": [ + { + "condition": "[variables('ResourceGroupCreate')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('ResourceGroupName')]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('ResourceGroupName')]" + }, + "location": { + "value": "[parameters('Location')]" + }, + "enableDefaultTelemetry": { + "value": false + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Resources/resourceGroups'), createObject('value', parameters('Tags')['Microsoft.Resources/resourceGroups']), createObject('value', createObject()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "14479610109813008203" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Resource Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. Location of the Resource Group. It uses the deployment's location when not provided." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the storage account resource." + } + }, + "managedBy": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The ID of the resource that manages this resource group." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "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": [] + } + } + }, + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2021-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "managedBy": "[parameters('managedBy')]", + "properties": {} + }, + { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-Lock', uniqueString(deployment().name, parameters('location')), parameters('lock'))]", + "resourceGroup": "[parameters('name')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "level": { + "value": "[parameters('lock')]" + }, + "name": { + "value": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "727668444186100245" + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-lock', parameters('level'))]", + "metadata": { + "description": "Optional. The name of the lock." + } + }, + "level": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Required. Set lock level." + } + }, + "notes": { + "type": "string", + "defaultValue": "[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]", + "metadata": { + "description": "Optional. The decription attached to the lock." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "name": "[parameters('name')]", + "properties": { + "level": "[parameters('level')]", + "notes": "[parameters('notes')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the lock." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the lock." + }, + "value": "[resourceId('Microsoft.Authorization/locks', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group name the lock was applied to." + }, + "value": "[resourceGroup().name]" + }, + "scope": { + "type": "string", + "metadata": { + "description": "The scope this lock applies to." + }, + "value": "[resourceGroup().id]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" + ] + }, + { + "copy": { + "name": "resourceGroup_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-RG-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[parameters('name')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), 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.23.1.45101", + "templateHash": "13976546302901379815" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "defaultValue": "[resourceGroup().id]", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Autonomous Development Platform Data Contributor (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b8b15564-4fa6-4a59-ab12-03e1d9594795')]", + "Autonomous Development Platform Data Owner (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '27f8b550-c507-4db9-86f2-f4b8e816d59d')]", + "Autonomous Development Platform Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd63b75f7-47ea-4f27-92ac-e0d173aaf093')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Avere Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "Azure Center for SAP solutions service role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138')]", + "Azure Connected Machine Resource Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cd570a14-e51a-42ad-bac8-bafd67325302')]", + "Azure Extension for SQL Server Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7392c568-9289-4bde-aaaa-b7131215889d')]", + "Azure Front Door Domain Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0ab34830-df19-4f8c-b84e-aa85b8afa6e8')]", + "Azure Front Door Domain Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f99d363-226e-4dca-9920-b807cf8e1a5f')]", + "Azure Front Door Secret Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3f2eb865-5811-4578-b90a-6fc6fa0df8e5')]", + "Azure Front Door Secret Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0db238c4-885e-4c4f-a933-aa2cef684fca')]", + "Azure Kubernetes Fleet Manager Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63bb64ad-9799-4770-b5c3-24ed299a07bf')]", + "Azure Kubernetes Fleet Manager RBAC Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '434fb43a-c01c-447e-9f67-c3ad923cfaba')]", + "Azure Kubernetes Fleet Manager RBAC Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ab4d3d-a1bf-4477-8ad9-8359bc988f69')]", + "Azure Kubernetes Fleet Manager RBAC Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '30b27cfc-9c84-438e-b0ce-70e35255df80')]", + "Azure Kubernetes Fleet Manager RBAC Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5af6afb3-c06c-4fa4-8848-71a8aee05683')]", + "Azure Kubernetes Service Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8')]", + "Azure Kubernetes Service Policy Add-on Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064')]", + "Azure Kubernetes Service RBAC Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3498e952-d568-435e-9b2c-8d77e338d7f7')]", + "Azure Kubernetes Service RBAC Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]", + "Azure Kubernetes Service RBAC Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f6c6a51-bcf8-42ba-9220-52d62157d7db')]", + "Azure Kubernetes Service RBAC Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb')]", + "Azure Maps Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dba33070-676a-4fb0-87fa-064dc56ff7fb')]", + "Azure Stack HCI registration role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bda0d508-adf1-4af0-9c28-88919fc3ae06')]", + "Azure Traffic Controller Configuration Manager": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbc52c3f-28ad-4303-a892-8a056630b8f1')]", + "Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b')]", + "Backup Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "Blueprint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '41077137-e803-4205-871c-5a86e6a753b4')]", + "Blueprint Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '437d2ced-4a38-4302-8479-ed2bcb43d090')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Chamber Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4e9b8407-af2e-495b-ae54-bb60a55b1b5a')]", + "Chamber User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4447db05-44ed-4da3-ae60-6cbece780e32')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Code Signing Certificate Profile Signer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2837e146-70d7-4cfd-ad55-7efa6464f958')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Cost Management Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '434105ed-43f6-45c7-a02f-909b2ba83430')]", + "Cost Management Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '72fafb9e-0641-4937-9268-a91bfd8191a3')]", + "Data Box Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'add466c9-e687-43fc-8d98-dfcf8d720be5')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Deployment Environments User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e40d4e-8d2e-438d-97e1-9528336e149c')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "DevCenter Dev Box User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45d50f46-0b78-4001-a660-4198cbe8cd05')]", + "DevCenter Project Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '331c37c6-af14-46d9-b9f4-e1909e1b95a0')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "DevTest Labs User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64')]", + "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')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Elastic SAN Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '80dcbedb-47ef-405d-95bd-188a1b4ac406')]", + "Elastic SAN Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'af6a70f8-3c9f-4105-acf1-d719e9fca4ca')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid Data Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a91429-5739-47e2-a06b-3470a27159e7')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "EventGrid EventSubscription Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2414bbcf-6497-4faf-8c65-045460748405')]", + "Experimentation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f646f1b-fa08-80eb-a33b-edd6ce5c915c')]", + "Experimentation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f646f1b-fa08-80eb-a22b-edd6ce5c915c')]", + "Guest Configuration Resource Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '088ab73d-1256-47ae-bea9-9de8e7131f31')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Lab Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "PlayFab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c8b84dc-067c-4039-9615-fa1a4b77c726')]", + "PlayFab Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a9a19cc5-31f4-447c-901f-56c0bb18fcaf')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Reservation Purchaser": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "Services Hub Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '82200a5b-e217-47a5-b665-6d8765ee745b')]", + "SignalR AccessKey Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Support Request Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Template Spec Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c9b6475-caf0-4164-b5a1-2142a7116f4b')]", + "Template Spec Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '392ae280-861d-42bd-9ea5-08ee6d83b80e')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('resourceId'), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the resource group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the resource group." + }, + "value": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2021-04-01', 'full').location]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_AutomtnAcct-{0}', variables('AutomationAccountName'))]", + "resourceGroup": "[parameters('ResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "diagnosticLogCategoriesToEnable": { + "value": [ + "JobLogs", + "JobStreams" + ] + }, + "enableDefaultTelemetry": { + "value": false + }, + "diagnosticWorkspaceId": { + "value": "[parameters('LogAnalyticsWorkspaceResourceId')]" + }, + "name": { + "value": "[variables('AutomationAccountName')]" + }, + "jobSchedules": "[if(not(empty(parameters('StorageAccountResourceIds'))), createObject('value', createArray(createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-0', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-1', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-2', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-3', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsAzFiles'), 'runbookName', variables('RunbookNameGetStorage'), 'scheduleName', format('{0}AzFilesStor-0', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsAzFiles'), 'runbookName', variables('RunbookNameGetStorage'), 'scheduleName', format('{0}AzFilesStor-1', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsAzFiles'), 'runbookName', variables('RunbookNameGetStorage'), 'scheduleName', format('{0}AzFilesStor-2', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsAzFiles'), 'runbookName', variables('RunbookNameGetStorage'), 'scheduleName', format('{0}AzFilesStor-3', variables('varScheduleName'))))), createObject('value', createArray(createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-0', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-1', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-2', variables('varScheduleName'))), createObject('parameters', variables('varJobScheduleParamsHostPool'), 'runbookName', variables('RunbookNameGetHostPool'), 'scheduleName', format('{0}HostPool-3', variables('varScheduleName'))))))]", + "location": { + "value": "[parameters('Location')]" + }, + "runbooks": "[if(not(empty(parameters('StorageAccountResourceIds'))), createObject('value', createArray(createObject('name', variables('RunbookNameGetHostPool'), 'description', 'AVD Metrics Runbook for collecting related Host Pool statistics to store in Log Analytics for specified Alert Queries', 'type', 'PowerShell', 'uri', format('{0}{1}', parameters('_ArtifactsLocation'), variables('RunbookScriptGetHostPool')), 'version', '1.0.0.0'), createObject('name', variables('RunbookNameGetStorage'), 'description', 'AVD Metrics Runbook for collecting related Azure Files storage statistics to store in Log Analytics for specified Alert Queries', 'type', 'PowerShell', 'uri', format('{0}{1}', parameters('_ArtifactsLocation'), variables('RunbookScriptGetStorage')), 'version', '1.0.0.0'))), createObject('value', createArray(createObject('name', variables('RunbookNameGetHostPool'), 'description', 'AVD Metrics Runbook for collecting related Host Pool statistics to store in Log Analytics for specified Alert Queries', 'type', 'PowerShell', 'uri', format('{0}{1}', parameters('_ArtifactsLocation'), variables('RunbookScriptGetHostPool')), 'version', '1.0.0.0'))))]", + "schedules": "[if(not(empty(parameters('StorageAccountResourceIds'))), createObject('value', createArray(createObject('name', format('{0}HostPool-0', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT15M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}HostPool-1', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT30M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}HostPool-2', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT45M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}HostPool-3', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT60M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}AzFilesStor-0', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT15M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}AzFilesStor-1', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT30M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}AzFilesStor-2', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT45M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}AzFilesStor-3', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT60M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()))), createObject('value', createArray(createObject('name', format('{0}HostPool-0', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT15M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}HostPool-1', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT30M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}HostPool-2', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT45M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()), createObject('name', format('{0}HostPool-3', variables('varScheduleName')), 'frequency', 'Hour', 'interval', 1, 'startTime', dateTimeAdd(parameters('time'), 'PT60M'), 'TimeZone', variables('varTimeZone'), 'advancedSchedule', createObject()))))]", + "skuName": { + "value": "Free" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Automation/automationAccounts'), createObject('value', parameters('Tags')['Microsoft.Automation/automationAccounts']), createObject('value', createObject()))]", + "systemAssignedIdentity": { + "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.23.1.45101", + "templateHash": "13669706201001327225" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Automation Account." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Free", + "Basic" + ], + "metadata": { + "description": "Optional. SKU name of the account." + } + }, + "cMKKeyVaultResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty." + } + }, + "cMKKeyName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the customer managed key to use for encryption." + } + }, + "cMKUserAssignedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty." + } + }, + "cMKKeyVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used." + } + }, + "modules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of modules to be created in the automation account." + } + }, + "runbooks": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of runbooks to be created in the automation account." + } + }, + "schedules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of schedules to be created in the automation account." + } + }, + "jobSchedules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of jobSchedules to be created in the automation account." + } + }, + "variables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of variables to be created in the automation account." + } + }, + "linkedWorkspaceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the log analytics workspace to be linked to the deployed automation account." + } + }, + "gallerySolutions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of gallerySolutions to be created in the linked log analytics workspace." + } + }, + "softwareUpdateConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of softwareUpdateConfigurations to be created in the automation account." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Disable local authentication profile used within the resource." + } + }, + "privateEndpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "diagnosticLogsRetentionInDays": { + "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." + } + }, + "diagnosticStorageAccountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account." + } + }, + "diagnosticWorkspaceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace." + } + }, + "diagnosticEventHubAuthorizationRuleId": { + "type": "string", + "defaultValue": "", + "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." + } + }, + "diagnosticEventHubName": { + "type": "string", + "defaultValue": "", + "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." + } + }, + "systemAssignedIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedIdentities": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The ID(s) to assign to the resource." + } + }, + "lock": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the Automation Account resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + }, + "diagnosticLogCategoriesToEnable": { + "type": "array", + "defaultValue": [ + "allLogs" + ], + "allowedValues": [ + "allLogs", + "JobLogs", + "JobStreams", + "DscNodeStatus" + ], + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource." + } + }, + "diagnosticMetricsToEnable": { + "type": "array", + "defaultValue": [ + "AllMetrics" + ], + "allowedValues": [ + "AllMetrics" + ], + "metadata": { + "description": "Optional. The name of metrics that will be streamed." + } + }, + "diagnosticSettingsName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to \"-diagnosticSettings\"." + } + } + }, + "variables": { + "copy": [ + { + "name": "diagnosticsLogsSpecified", + "count": "[length(filter(parameters('diagnosticLogCategoriesToEnable'), lambda('item', not(equals(lambdaVariables('item'), 'allLogs')))))]", + "input": { + "category": "[filter(parameters('diagnosticLogCategoriesToEnable'), lambda('item', not(equals(lambdaVariables('item'), 'allLogs'))))[copyIndex('diagnosticsLogsSpecified')]]", + "enabled": true + } + }, + { + "name": "diagnosticsMetrics", + "count": "[length(parameters('diagnosticMetricsToEnable'))]", + "input": { + "category": "[parameters('diagnosticMetricsToEnable')[copyIndex('diagnosticsMetrics')]]", + "timeGrain": null, + "enabled": true + } + } + ], + "enableReferencedModulesTelemetry": false, + "diagnosticsLogs": "[if(contains(parameters('diagnosticLogCategoriesToEnable'), 'allLogs'), createArray(createObject('categoryGroup', 'allLogs', 'enabled', true())), variables('diagnosticsLogsSpecified'))]", + "identityType": "[if(parameters('systemAssignedIdentity'), if(not(empty(parameters('userAssignedIdentities'))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(parameters('userAssignedIdentities'))), 'UserAssigned', 'None'))]", + "identity": "[if(not(equals(variables('identityType'), 'None')), createObject('type', variables('identityType'), 'userAssignedIdentities', if(not(empty(parameters('userAssignedIdentities'))), parameters('userAssignedIdentities'), null())), null())]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts", + "apiVersion": "2022-08-08", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "sku": { + "name": "[parameters('skuName')]" + }, + "encryption": "[if(not(empty(parameters('cMKKeyName'))), createObject('keySource', 'Microsoft.KeyVault', 'identity', createObject('userAssignedIdentity', parameters('cMKUserAssignedIdentityResourceId')), 'keyVaultProperties', createObject('keyName', parameters('cMKKeyName'), 'keyVaultUri', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('cMKKeyVaultResourceId'), '/')[2], split(parameters('cMKKeyVaultResourceId'), '/')[4]), 'Microsoft.KeyVault/vaults', last(split(parameters('cMKKeyVaultResourceId'), '/'))), '2021-10-01').vaultUri, 'keyVersion', if(not(empty(parameters('cMKKeyVersion'))), parameters('cMKKeyVersion'), last(split(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('cMKKeyVaultResourceId'), '/')[2], split(parameters('cMKKeyVaultResourceId'), '/')[4]), 'Microsoft.KeyVault/vaults/keys', split(format('{0}/{1}', last(split(parameters('cMKKeyVaultResourceId'), '/')), parameters('cMKKeyName')), '/')[0], split(format('{0}/{1}', last(split(parameters('cMKKeyVaultResourceId'), '/')), parameters('cMKKeyName')), '/')[1]), '2021-10-01').keyUriWithVersion, '/'))))), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), if(equals(parameters('publicNetworkAccess'), 'Disabled'), false(), true()), if(not(empty(parameters('privateEndpoints'))), false(), null()))]", + "disableLocalAuth": "[parameters('disableLocalAuth')]" + } + }, + { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Automation/automationAccounts/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "condition": "[or(or(or(not(empty(parameters('diagnosticStorageAccountId'))), not(empty(parameters('diagnosticWorkspaceId')))), not(empty(parameters('diagnosticEventHubAuthorizationRuleId')))), not(empty(parameters('diagnosticEventHubName'))))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Automation/automationAccounts/{0}', parameters('name'))]", + "name": "[if(not(empty(parameters('diagnosticSettingsName'))), parameters('diagnosticSettingsName'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "storageAccountId": "[if(not(empty(parameters('diagnosticStorageAccountId'))), parameters('diagnosticStorageAccountId'), null())]", + "workspaceId": "[if(not(empty(parameters('diagnosticWorkspaceId'))), parameters('diagnosticWorkspaceId'), null())]", + "eventHubAuthorizationRuleId": "[if(not(empty(parameters('diagnosticEventHubAuthorizationRuleId'))), parameters('diagnosticEventHubAuthorizationRuleId'), null())]", + "eventHubName": "[if(not(empty(parameters('diagnosticEventHubName'))), parameters('diagnosticEventHubName'), null())]", + "metrics": "[variables('diagnosticsMetrics')]", + "logs": "[variables('diagnosticsLogs')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "copy": { + "name": "automationAccount_modules", + "count": "[length(parameters('modules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-Module-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('modules')[copyIndex()].name]" + }, + "automationAccountName": { + "value": "[parameters('name')]" + }, + "version": { + "value": "[parameters('modules')[copyIndex()].version]" + }, + "uri": { + "value": "[parameters('modules')[copyIndex()].uri]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "9873337138567935156" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Automation Account module." + } + }, + "automationAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment." + } + }, + "uri": { + "type": "string", + "metadata": { + "description": "Required. Module package URI, e.g. https://www.powershellgallery.com/api/v2/package." + } + }, + "version": { + "type": "string", + "defaultValue": "latest", + "metadata": { + "description": "Optional. Module version or specify latest to get the latest version." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the Automation Account resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts/modules", + "apiVersion": "2022-08-08", + "name": "[format('{0}/{1}', parameters('automationAccountName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "contentLink": { + "uri": "[if(not(equals(parameters('version'), 'latest')), format('{0}/{1}/{2}', parameters('uri'), parameters('name'), parameters('version')), format('{0}/{1}', parameters('uri'), parameters('name')))]", + "version": "[if(not(equals(parameters('version'), 'latest')), parameters('version'), null())]" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed module." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed module." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts/modules', parameters('automationAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed module." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Automation/automationAccounts/modules', parameters('automationAccountName'), parameters('name')), '2022-08-08', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "copy": { + "name": "automationAccount_schedules", + "count": "[length(parameters('schedules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-Schedule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('schedules')[copyIndex()].name]" + }, + "automationAccountName": { + "value": "[parameters('name')]" + }, + "advancedSchedule": "[if(contains(parameters('schedules')[copyIndex()], 'advancedSchedule'), createObject('value', parameters('schedules')[copyIndex()].advancedSchedule), createObject('value', null()))]", + "description": "[if(contains(parameters('schedules')[copyIndex()], 'description'), createObject('value', parameters('schedules')[copyIndex()].description), createObject('value', ''))]", + "expiryTime": "[if(contains(parameters('schedules')[copyIndex()], 'expiryTime'), createObject('value', parameters('schedules')[copyIndex()].expiryTime), createObject('value', ''))]", + "frequency": "[if(contains(parameters('schedules')[copyIndex()], 'frequency'), createObject('value', parameters('schedules')[copyIndex()].frequency), createObject('value', 'OneTime'))]", + "interval": "[if(contains(parameters('schedules')[copyIndex()], 'interval'), createObject('value', parameters('schedules')[copyIndex()].interval), createObject('value', 0))]", + "startTime": "[if(contains(parameters('schedules')[copyIndex()], 'startTime'), createObject('value', parameters('schedules')[copyIndex()].startTime), createObject('value', ''))]", + "timeZone": "[if(contains(parameters('schedules')[copyIndex()], 'timeZone'), createObject('value', parameters('schedules')[copyIndex()].timeZone), createObject('value', ''))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "12742772183632552384" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Automation Account schedule." + } + }, + "automationAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment." + } + }, + "advancedSchedule": { + "type": "object", + "defaultValue": {}, + "metadata": { + "monthDays": "Days of the month that the job should execute on. Must be between 1 and 31.", + "monthlyOccurrences": "Occurrences of days within a month.", + "weekDays": "Days of the week that the job should execute on.", + "description": "Optional. The properties of the create Advanced Schedule." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the schedule." + } + }, + "expiryTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The end time of the schedule." + } + }, + "frequency": { + "type": "string", + "defaultValue": "OneTime", + "allowedValues": [ + "Day", + "Hour", + "Minute", + "Month", + "OneTime", + "Week" + ], + "metadata": { + "description": "Optional. The frequency of the schedule." + } + }, + "interval": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Anything." + } + }, + "startTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The start time of the schedule." + } + }, + "timeZone": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The time zone of the schedule." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('u')]", + "metadata": { + "description": "Generated. Time used as a basis for e.g. the schedule start date." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts/schedules", + "apiVersion": "2022-08-08", + "name": "[format('{0}/{1}', parameters('automationAccountName'), parameters('name'))]", + "properties": { + "advancedSchedule": "[if(not(empty(parameters('advancedSchedule'))), parameters('advancedSchedule'), null())]", + "description": "[if(not(empty(parameters('description'))), parameters('description'), null())]", + "expiryTime": "[if(not(empty(parameters('expiryTime'))), parameters('expiryTime'), null())]", + "frequency": "[if(not(empty(parameters('frequency'))), parameters('frequency'), 'OneTime')]", + "interval": "[if(not(equals(parameters('interval'), 0)), parameters('interval'), null())]", + "startTime": "[if(not(empty(parameters('startTime'))), parameters('startTime'), dateTimeAdd(parameters('baseTime'), 'PT10M'))]", + "timeZone": "[if(not(empty(parameters('timeZone'))), parameters('timeZone'), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed schedule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed schedule." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts/schedules', parameters('automationAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed schedule." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "copy": { + "name": "automationAccount_runbooks", + "count": "[length(parameters('runbooks'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-Runbook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('runbooks')[copyIndex()].name]" + }, + "automationAccountName": { + "value": "[parameters('name')]" + }, + "type": { + "value": "[parameters('runbooks')[copyIndex()].type]" + }, + "description": "[if(contains(parameters('runbooks')[copyIndex()], 'description'), createObject('value', parameters('runbooks')[copyIndex()].description), createObject('value', ''))]", + "uri": "[if(contains(parameters('runbooks')[copyIndex()], 'uri'), createObject('value', parameters('runbooks')[copyIndex()].uri), createObject('value', ''))]", + "version": "[if(contains(parameters('runbooks')[copyIndex()], 'version'), createObject('value', parameters('runbooks')[copyIndex()].version), createObject('value', ''))]", + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "11768955385651742227" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Automation Account runbook." + } + }, + "automationAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "Graph", + "GraphPowerShell", + "GraphPowerShellWorkflow", + "PowerShell", + "PowerShellWorkflow" + ], + "metadata": { + "description": "Required. The type of the runbook." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the runbook." + } + }, + "uri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The uri of the runbook content." + } + }, + "version": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The version of the runbook content." + } + }, + "scriptStorageAccountId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. ID of the runbook storage account." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('u')]", + "metadata": { + "description": "Generated. Time used as a basis for e.g. the schedule start date." + } + }, + "sasTokenValidityLength": { + "type": "string", + "defaultValue": "PT8H", + "metadata": { + "description": "Optional. SAS token validity length. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the Automation Account resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "accountSasProperties": { + "signedServices": "b", + "signedPermission": "r", + "signedExpiry": "[dateTimeAdd(parameters('baseTime'), parameters('sasTokenValidityLength'))]", + "signedResourceTypes": "o", + "signedProtocol": "https" + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts/runbooks", + "apiVersion": "2022-08-08", + "name": "[format('{0}/{1}', parameters('automationAccountName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "runbookType": "[parameters('type')]", + "description": "[parameters('description')]", + "publishContentLink": "[if(not(empty(parameters('uri'))), if(empty(parameters('uri')), null(), createObject('uri', if(not(empty(parameters('uri'))), if(empty(parameters('scriptStorageAccountId')), parameters('uri'), format('{0}?{1}', parameters('uri'), listAccountSas(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('scriptStorageAccountId'), '/')[2], split(parameters('scriptStorageAccountId'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(parameters('scriptStorageAccountId'), '/'))), '2021-04-01', variables('accountSasProperties')).accountSasToken)), null()), 'version', if(not(empty(parameters('version'))), parameters('version'), null()))), null())]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed runbook." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed runbook." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts/runbooks', parameters('automationAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed runbook." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Automation/automationAccounts/runbooks', parameters('automationAccountName'), parameters('name')), '2022-08-08', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "copy": { + "name": "automationAccount_jobSchedules", + "count": "[length(parameters('jobSchedules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-JobSchedule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "automationAccountName": { + "value": "[parameters('name')]" + }, + "runbookName": { + "value": "[parameters('jobSchedules')[copyIndex()].runbookName]" + }, + "scheduleName": { + "value": "[parameters('jobSchedules')[copyIndex()].scheduleName]" + }, + "parameters": "[if(contains(parameters('jobSchedules')[copyIndex()], 'parameters'), createObject('value', parameters('jobSchedules')[copyIndex()].parameters), createObject('value', createObject()))]", + "runOn": "[if(contains(parameters('jobSchedules')[copyIndex()], 'runOn'), createObject('value', parameters('jobSchedules')[copyIndex()].runOn), createObject('value', ''))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "5809431067482816549" + } + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[newGuid()]", + "metadata": { + "description": "Generated. Name of the Automation Account job schedule. Must be a GUID and is autogenerated. No need to provide this value." + } + }, + "automationAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment." + } + }, + "runbookName": { + "type": "string", + "metadata": { + "description": "Required. The runbook property associated with the entity." + } + }, + "scheduleName": { + "type": "string", + "metadata": { + "description": "Required. The schedule property associated with the entity." + } + }, + "parameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. List of job properties." + } + }, + "runOn": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The hybrid worker group that the scheduled job should run on." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts/jobSchedules", + "apiVersion": "2022-08-08", + "name": "[format('{0}/{1}', parameters('automationAccountName'), parameters('name'))]", + "properties": { + "parameters": "[parameters('parameters')]", + "runbook": { + "name": "[parameters('runbookName')]" + }, + "runOn": "[if(not(empty(parameters('runOn'))), parameters('runOn'), null())]", + "schedule": { + "name": "[parameters('scheduleName')]" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed job schedule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed job schedule." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts/jobSchedules', parameters('automationAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed job schedule." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]", + "automationAccount_runbooks", + "automationAccount_schedules" + ] + }, + { + "copy": { + "name": "automationAccount_variables", + "count": "[length(parameters('variables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-Variable-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "automationAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('variables')[copyIndex()].name]" + }, + "description": "[if(contains(parameters('variables')[copyIndex()], 'description'), createObject('value', parameters('variables')[copyIndex()].description), createObject('value', ''))]", + "value": { + "value": "[parameters('variables')[copyIndex()].value]" + }, + "isEncrypted": "[if(contains(parameters('variables')[copyIndex()], 'isEncrypted'), createObject('value', parameters('variables')[copyIndex()].isEncrypted), createObject('value', true()))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "15421173239527809626" + } + }, + "parameters": { + "automationAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the variable." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the variable. For security best practices, this value is always passed as a secure string as it could contain an encrypted value when the \"isEncrypted\" property is set to true." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the variable." + } + }, + "isEncrypted": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If the variable should be encrypted. For security reasons encryption of variables should be enabled." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts/variables", + "apiVersion": "2022-08-08", + "name": "[format('{0}/{1}', parameters('automationAccountName'), parameters('name'))]", + "properties": { + "description": "[parameters('description')]", + "isEncrypted": "[parameters('isEncrypted')]", + "value": "[parameters('value')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed variable." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed variable." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts/variables', parameters('automationAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed variable." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "condition": "[not(empty(parameters('linkedWorkspaceResourceId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-LinkedService', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[if(not(empty(parameters('linkedWorkspaceResourceId'))), split(parameters('linkedWorkspaceResourceId'), '/')[2], subscription().subscriptionId)]", + "resourceGroup": "[if(not(empty(parameters('linkedWorkspaceResourceId'))), split(parameters('linkedWorkspaceResourceId'), '/')[4], resourceGroup().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "automation" + }, + "logAnalyticsWorkspaceName": { + "value": "[last(split(parameters('linkedWorkspaceResourceId'), '/'))]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "resourceId": { + "value": "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "8116463202302820849" + } + }, + "parameters": { + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the link." + } + }, + "resourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access." + } + }, + "writeAccessResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/linkedServices", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resourceId": "[parameters('resourceId')]", + "writeAccessResourceId": "[if(empty(parameters('writeAccessResourceId')), null(), parameters('writeAccessResourceId'))]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed linked service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed linked service." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/linkedServices', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the linked service is deployed." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "copy": { + "name": "automationAccount_solutions", + "count": "[length(parameters('gallerySolutions'))]" + }, + "condition": "[not(empty(parameters('linkedWorkspaceResourceId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-Solution-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[if(not(empty(parameters('linkedWorkspaceResourceId'))), split(parameters('linkedWorkspaceResourceId'), '/')[2], subscription().subscriptionId)]", + "resourceGroup": "[if(not(empty(parameters('linkedWorkspaceResourceId'))), split(parameters('linkedWorkspaceResourceId'), '/')[4], resourceGroup().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('gallerySolutions')[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[last(split(parameters('linkedWorkspaceResourceId'), '/'))]" + }, + "product": "[if(contains(parameters('gallerySolutions')[copyIndex()], 'product'), createObject('value', parameters('gallerySolutions')[copyIndex()].product), createObject('value', 'OMSGallery'))]", + "publisher": "[if(contains(parameters('gallerySolutions')[copyIndex()], 'publisher'), createObject('value', parameters('gallerySolutions')[copyIndex()].publisher), createObject('value', 'Microsoft'))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "10708379588686916495" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the solution. For Microsoft published gallery solution the target solution resource name will be composed as `{name}({logAnalyticsWorkspaceName})`." + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Log Analytics workspace where the solution will be deployed/enabled." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "product": { + "type": "string", + "defaultValue": "OMSGallery", + "metadata": { + "description": "Optional. The product of the deployed solution. For Microsoft published gallery solution it should be `OMSGallery` and the target solution resource product will be composed as `OMSGallery/{name}`. For third party solution, it can be anything. This is case sensitive." + } + }, + "publisher": { + "type": "string", + "defaultValue": "Microsoft", + "metadata": { + "description": "Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "solutionName": "[if(equals(parameters('publisher'), 'Microsoft'), format('{0}({1})', parameters('name'), parameters('logAnalyticsWorkspaceName')), parameters('name'))]", + "solutionProduct": "[if(equals(parameters('publisher'), 'Microsoft'), format('OMSGallery/{0}', parameters('name')), parameters('product'))]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.OperationsManagement/solutions", + "apiVersion": "2015-11-01-preview", + "name": "[variables('solutionName')]", + "location": "[parameters('location')]", + "properties": { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" + }, + "plan": { + "name": "[variables('solutionName')]", + "promotionCode": "", + "product": "[variables('solutionProduct')]", + "publisher": "[parameters('publisher')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed solution." + }, + "value": "[variables('solutionName')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed solution." + }, + "value": "[resourceId('Microsoft.OperationsManagement/solutions', variables('solutionName'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the solution is deployed." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.OperationsManagement/solutions', variables('solutionName')), '2015-11-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', if(not(empty(parameters('linkedWorkspaceResourceId'))), split(parameters('linkedWorkspaceResourceId'), '/')[2], subscription().subscriptionId), if(not(empty(parameters('linkedWorkspaceResourceId'))), split(parameters('linkedWorkspaceResourceId'), '/')[4], resourceGroup().name)), 'Microsoft.Resources/deployments', format('{0}-AutoAccount-LinkedService', uniqueString(deployment().name, parameters('location'))))]" + ] + }, + { + "copy": { + "name": "automationAccount_softwareUpdateConfigurations", + "count": "[length(parameters('softwareUpdateConfigurations'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-SwUpdateConfig-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('softwareUpdateConfigurations')[copyIndex()].name]" + }, + "automationAccountName": { + "value": "[parameters('name')]" + }, + "frequency": { + "value": "[parameters('softwareUpdateConfigurations')[copyIndex()].frequency]" + }, + "operatingSystem": { + "value": "[parameters('softwareUpdateConfigurations')[copyIndex()].operatingSystem]" + }, + "rebootSetting": { + "value": "[parameters('softwareUpdateConfigurations')[copyIndex()].rebootSetting]" + }, + "azureVirtualMachines": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'azureVirtualMachines'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].azureVirtualMachines), createObject('value', createArray()))]", + "excludeUpdates": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'excludeUpdates'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].excludeUpdates), createObject('value', createArray()))]", + "expiryTime": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'expiryTime'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].expiryTime), createObject('value', ''))]", + "expiryTimeOffsetMinutes": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'expiryTimeOffsetMinutes'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].expiryTimeOffsetMinute), createObject('value', 0))]", + "includeUpdates": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'includeUpdates'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].includeUpdates), createObject('value', createArray()))]", + "interval": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'interval'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].interval), createObject('value', 1))]", + "isEnabled": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'isEnabled'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].isEnabled), createObject('value', true()))]", + "maintenanceWindow": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'maintenanceWindow'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].maintenanceWindow), createObject('value', 'PT2H'))]", + "monthDays": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'monthDays'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].monthDays), createObject('value', createArray()))]", + "monthlyOccurrences": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'monthlyOccurrences'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].monthlyOccurrences), createObject('value', createArray()))]", + "nextRun": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'nextRun'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].nextRun), createObject('value', ''))]", + "nextRunOffsetMinutes": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'nextRunOffsetMinutes'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].nextRunOffsetMinutes), createObject('value', 0))]", + "nonAzureComputerNames": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'nonAzureComputerNames'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].nonAzureComputerNames), createObject('value', createArray()))]", + "nonAzureQueries": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'nonAzureQueries'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].nonAzureQueries), createObject('value', createArray()))]", + "postTaskParameters": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'postTaskParameters'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].postTaskParameters), createObject('value', createObject()))]", + "postTaskSource": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'postTaskSource'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].postTaskSource), createObject('value', ''))]", + "preTaskParameters": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'preTaskParameters'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].preTaskParameters), createObject('value', createObject()))]", + "preTaskSource": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'preTaskSource'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].preTaskSource), createObject('value', ''))]", + "scheduleDescription": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'scheduleDescription'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].scheduleDescription), createObject('value', ''))]", + "scopeByLocations": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'scopeByLocations'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].scopeByLocations), createObject('value', createArray()))]", + "scopeByResources": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'scopeByResources'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].scopeByResources), createObject('value', createArray(subscription().id)))]", + "scopeByTags": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'scopeByTags'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].scopeByTags), createObject('value', createObject()))]", + "scopeByTagsOperation": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'scopeByTagsOperation'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].scopeByTagsOperation), createObject('value', 'All'))]", + "startTime": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'startTime'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].startTime), createObject('value', ''))]", + "timeZone": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'timeZone'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].timeZone), createObject('value', 'UTC'))]", + "updateClassifications": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'updateClassifications'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].updateClassifications), createObject('value', createArray('Critical', 'Security')))]", + "weekDays": "[if(contains(parameters('softwareUpdateConfigurations')[copyIndex()], 'weekDays'), createObject('value', parameters('softwareUpdateConfigurations')[copyIndex()].weekDays), createObject('value', createArray()))]", + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "3765098102914952048" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Deployment schedule." + } + }, + "automationAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment." + } + }, + "operatingSystem": { + "type": "string", + "allowedValues": [ + "Windows", + "Linux" + ], + "metadata": { + "description": "Required. The operating system to be configured by the deployment schedule." + } + }, + "rebootSetting": { + "type": "string", + "allowedValues": [ + "IfRequired", + "Never", + "RebootOnly", + "Always" + ], + "metadata": { + "description": "Required. Reboot setting for the deployment schedule." + } + }, + "frequency": { + "type": "string", + "allowedValues": [ + "OneTime", + "Hour", + "Day", + "Week", + "Month" + ], + "metadata": { + "description": "Required. The frequency of the deployment schedule. When using 'Hour', 'Day', 'Week' or 'Month', an interval needs to be provided." + } + }, + "maintenanceWindow": { + "type": "string", + "defaultValue": "PT2H", + "metadata": { + "description": "Optional. Maximum time allowed for the deployment schedule to run. Duration needs to be specified using the format PT[n]H[n]M[n]S as per ISO8601." + } + }, + "updateClassifications": { + "type": "array", + "defaultValue": [ + "Critical", + "Security" + ], + "allowedValues": [ + "Critical", + "Security", + "UpdateRollup", + "FeaturePack", + "ServicePack", + "Definition", + "Tools", + "Updates", + "Other" + ], + "metadata": { + "description": "Optional. Update classification included in the deployment schedule." + } + }, + "excludeUpdates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. KB numbers or Linux packages excluded in the deployment schedule." + } + }, + "includeUpdates": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. KB numbers or Linux packages included in the deployment schedule." + } + }, + "scopeByResources": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. Specify the resources to scope the deployment schedule to." + } + }, + "scopeByTags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Specify tags to which to scope the deployment schedule to." + } + }, + "scopeByTagsOperation": { + "type": "string", + "defaultValue": "All", + "allowedValues": [ + "All", + "Any" + ], + "metadata": { + "description": "Optional. Enables the scopeByTags to require All (Tag A and Tag B) or Any (Tag A or Tag B)." + } + }, + "scopeByLocations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specify locations to which to scope the deployment schedule to." + } + }, + "preTaskParameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters provided to the task running before the deployment schedule." + } + }, + "preTaskSource": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The source of the task running before the deployment schedule." + } + }, + "postTaskParameters": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters provided to the task running after the deployment schedule." + } + }, + "postTaskSource": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The source of the task running after the deployment schedule." + } + }, + "interval": { + "type": "int", + "defaultValue": 1, + "maxValue": 100, + "metadata": { + "description": "Optional. The interval of the frequency for the deployment schedule. 1 Hour is every hour, 2 Day is every second day, etc." + } + }, + "isEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enables the deployment schedule." + } + }, + "timeZone": { + "type": "string", + "defaultValue": "UTC", + "metadata": { + "description": "Optional. Time zone for the deployment schedule. IANA ID or a Windows Time Zone ID." + } + }, + "nonAzureQueries": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of functions from a Log Analytics workspace, used to scope the deployment schedule." + } + }, + "azureVirtualMachines": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of azure resource IDs for azure virtual machines in scope for the deployment schedule." + } + }, + "nonAzureComputerNames": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of names of non-azure machines in scope for the deployment schedule." + } + }, + "weekDays": { + "type": "array", + "defaultValue": [], + "allowedValues": [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday" + ], + "metadata": { + "description": "Optional. Required when used with frequency 'Week'. Specified the day of the week to run the deployment schedule." + } + }, + "monthDays": { + "type": "array", + "defaultValue": [], + "allowedValues": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "metadata": { + "description": "Optional. Can be used with frequency 'Month'. Provides the specific days of the month to run the deployment schedule." + } + }, + "monthlyOccurrences": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Can be used with frequency 'Month'. Provides the pattern/cadence for running the deployment schedule in a month. Takes objects formed like this {occurance(int),day(string)}. Day is the name of the day to run the deployment schedule, the occurance specifies which occurance of that day to run the deployment schedule." + } + }, + "startTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The start time of the deployment schedule in ISO 8601 format. To specify a specific time use YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. For schedules where we want to start the deployment as soon as possible, specify the time segment only in 24 hour format, HH:MM, 22:00." + } + }, + "expiryTime": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The end time of the deployment schedule in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00." + } + }, + "expiryTimeOffsetMinutes": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The expiry time's offset in minutes." + } + }, + "nextRun": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The next time the deployment schedule runs in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00." + } + }, + "nextRunOffsetMinutes": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The next run's offset in minutes." + } + }, + "scheduleDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The schedules description." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('u')]", + "metadata": { + "description": "Generated. Do not touch. Is used to provide the base time for time comparison for startTime. If startTime is specified in HH:MM format, baseTime is used to check if the provided startTime has passed, adding one day before setting the deployment schedule." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "updateClassificationsVar": "[replace(replace(replace(replace(string(parameters('updateClassifications')), ',', ', '), '[', ''), ']', ''), '\"', '')]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Automation/automationAccounts/softwareUpdateConfigurations", + "apiVersion": "2019-06-01", + "name": "[format('{0}/{1}', parameters('automationAccountName'), parameters('name'))]", + "properties": { + "updateConfiguration": { + "operatingSystem": "[parameters('operatingSystem')]", + "duration": "[parameters('maintenanceWindow')]", + "linux": "[if(equals(parameters('operatingSystem'), 'Linux'), createObject('excludedPackageNameMasks', parameters('excludeUpdates'), 'includedPackageNameMasks', parameters('includeUpdates'), 'includedPackageClassifications', variables('updateClassificationsVar'), 'rebootSetting', parameters('rebootSetting')), null())]", + "windows": "[if(equals(parameters('operatingSystem'), 'Windows'), createObject('excludedKbNumbers', parameters('excludeUpdates'), 'includedKbNumbers', parameters('includeUpdates'), 'includedUpdateClassifications', variables('updateClassificationsVar'), 'rebootSetting', parameters('rebootSetting')), null())]", + "targets": { + "azureQueries": [ + { + "scope": "[parameters('scopeByResources')]", + "tagSettings": { + "tags": "[parameters('scopeByTags')]", + "filterOperator": "[parameters('scopeByTagsOperation')]" + }, + "locations": "[parameters('scopeByLocations')]" + } + ], + "nonAzureQueries": "[parameters('nonAzureQueries')]" + }, + "azureVirtualMachines": "[parameters('azureVirtualMachines')]", + "nonAzureComputerNames": "[parameters('nonAzureComputerNames')]" + }, + "tasks": { + "preTask": { + "parameters": "[if(empty(parameters('preTaskParameters')), null(), parameters('preTaskParameters'))]", + "source": "[if(empty(parameters('preTaskSource')), null(), parameters('preTaskSource'))]" + }, + "postTask": { + "parameters": "[if(empty(parameters('postTaskParameters')), null(), parameters('postTaskParameters'))]", + "source": "[if(empty(parameters('postTaskSource')), null(), parameters('postTaskSource'))]" + } + }, + "scheduleInfo": { + "interval": "[parameters('interval')]", + "frequency": "[parameters('frequency')]", + "isEnabled": "[parameters('isEnabled')]", + "timeZone": "[parameters('timeZone')]", + "advancedSchedule": { + "weekDays": "[if(empty(parameters('weekDays')), null(), parameters('weekDays'))]", + "monthDays": "[if(empty(parameters('monthDays')), null(), parameters('monthDays'))]", + "monthlyOccurrences": "[if(empty(parameters('monthlyOccurrences')), null(), parameters('monthlyOccurrences'))]" + }, + "startTime": "[if(empty(parameters('startTime')), dateTimeAdd(parameters('baseTime'), 'PT10M'), parameters('startTime'))]", + "expiryTime": "[parameters('expiryTime')]", + "expiryTimeOffsetMinutes": "[parameters('expiryTimeOffsetMinutes')]", + "nextRun": "[parameters('nextRun')]", + "nextRunOffsetMinutes": "[parameters('nextRunOffsetMinutes')]", + "description": "[parameters('scheduleDescription')]" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed softwareUpdateConfiguration." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed softwareUpdateConfiguration." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts/softwareUpdateConfigurations', parameters('automationAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed softwareUpdateConfiguration." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]", + "automationAccount_solutions" + ] + }, + { + "copy": { + "name": "automationAccount_privateEndpoints", + "count": "[length(parameters('privateEndpoints'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutomationAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "groupIds": { + "value": [ + "[parameters('privateEndpoints')[copyIndex()].service]" + ] + }, + "name": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'name'), createObject('value', parameters('privateEndpoints')[copyIndex()].name), createObject('value', format('pe-{0}-{1}-{2}', last(split(resourceId('Microsoft.Automation/automationAccounts', parameters('name')), '/')), parameters('privateEndpoints')[copyIndex()].service, copyIndex())))]", + "serviceResourceId": { + "value": "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + }, + "subnetResourceId": { + "value": "[parameters('privateEndpoints')[copyIndex()].subnetResourceId]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[reference(split(parameters('privateEndpoints')[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location]" + }, + "lock": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'lock'), createObject('value', parameters('privateEndpoints')[copyIndex()].lock), createObject('value', parameters('lock')))]", + "privateDnsZoneGroup": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'privateDnsZoneGroup'), createObject('value', parameters('privateEndpoints')[copyIndex()].privateDnsZoneGroup), createObject('value', createObject()))]", + "roleAssignments": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'roleAssignments'), createObject('value', parameters('privateEndpoints')[copyIndex()].roleAssignments), createObject('value', createArray()))]", + "tags": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'tags'), createObject('value', parameters('privateEndpoints')[copyIndex()].tags), createObject('value', createObject()))]", + "manualPrivateLinkServiceConnections": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'manualPrivateLinkServiceConnections'), createObject('value', parameters('privateEndpoints')[copyIndex()].manualPrivateLinkServiceConnections), createObject('value', createArray()))]", + "customDnsConfigs": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'customDnsConfigs'), createObject('value', parameters('privateEndpoints')[copyIndex()].customDnsConfigs), createObject('value', createArray()))]", + "ipConfigurations": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'ipConfigurations'), createObject('value', parameters('privateEndpoints')[copyIndex()].ipConfigurations), createObject('value', createArray()))]", + "applicationSecurityGroups": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'applicationSecurityGroups'), createObject('value', parameters('privateEndpoints')[copyIndex()].applicationSecurityGroups), createObject('value', createArray()))]", + "customNetworkInterfaceName": "[if(contains(parameters('privateEndpoints')[copyIndex()], 'customNetworkInterfaceName'), createObject('value', parameters('privateEndpoints')[copyIndex()].customNetworkInterfaceName), 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.23.1.45101", + "templateHash": "5300610667995634254" + } + }, + "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." + } + }, + "serviceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource that needs to be connected to the network." + } + }, + "applicationSecurityGroups": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to." + } + }, + "privateDnsZoneGroup": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The private DNS zone group configuration used to associate the private endpoint with one or multiple private DNS zones. 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": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "CanNotDelete", + "ReadOnly" + ], + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "enableReferencedModulesTelemetry": false + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2022-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "applicationSecurityGroups": "[parameters('applicationSecurityGroups')]", + "customDnsConfigs": "[parameters('customDnsConfigs')]", + "customNetworkInterfaceName": "[parameters('customNetworkInterfaceName')]", + "ipConfigurations": "[parameters('ipConfigurations')]", + "manualPrivateLinkServiceConnections": "[parameters('manualPrivateLinkServiceConnections')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('name')]", + "properties": { + "privateLinkServiceId": "[parameters('serviceResourceId')]", + "groupIds": "[parameters('groupIds')]" + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + { + "condition": "[not(empty(parameters('lock')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[format('{0}-{1}-lock', parameters('name'), parameters('lock'))]", + "properties": { + "level": "[parameters('lock')]", + "notes": "[if(equals(parameters('lock'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + ] + }, + { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDNSResourceIds": { + "value": "[parameters('privateDnsZoneGroup').privateDNSResourceIds]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "enableDefaultTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "4621144128017741284" + } + }, + "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." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2022-07-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": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + ] + }, + { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Network/privateEndpoints', 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.23.1.45101", + "templateHash": "7828421530828782575" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Avere Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "Azure Center for SAP solutions service role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138')]", + "Azure Kubernetes Service Policy Add-on Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064')]", + "Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b')]", + "Backup Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "DevTest Labs User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "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')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Virtual Machine User Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52')]", + "Windows Admin Center Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + ] + } + ], + "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(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), '2022-07-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + }, + { + "copy": { + "name": "automationAccount_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AutoAccount-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Automation/automationAccounts', 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.23.1.45101", + "templateHash": "18129760119459600298" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Resource Policy Contributor": "[subscriptionResourceId('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')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Automation/automationAccounts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Automation/automationAccounts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed automation account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed automation account." + }, + "value": "[resourceId('Microsoft.Automation/automationAccounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed automation account." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[if(and(parameters('systemAssignedIdentity'), contains(reference(resourceId('Microsoft.Automation/automationAccounts', parameters('name')), '2022-08-08', 'full').identity, 'principalId')), reference(resourceId('Microsoft.Automation/automationAccounts', parameters('name')), '2022-08-08', 'full').identity.principalId, '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Automation/automationAccounts', parameters('name')), '2022-08-08', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', parameters('ResourceGroupName'))]" + ] + }, + { + "copy": { + "name": "roleAssignment_AutoAcctDesktopRead", + "count": "[length(parameters('HostPoolInfo'))]" + }, + "condition": "[not(parameters('AllResourcesSameRG'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_DsktpRead_{0}', split(parameters('HostPoolInfo')[copyIndex()].colVMResGroup, '/')[4])]", + "resourceGroup": "[split(parameters('HostPoolInfo')[copyIndex()].colVMResGroup, '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName'))), '2022-09-01').outputs.systemAssignedPrincipalId.value]" + }, + "roleDefinitionIdOrName": { + "value": "Desktop Virtualization Reader" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "[split(parameters('HostPoolInfo')[copyIndex()].colVMResGroup, '/')[4]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "9545798095452579480" + } + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition, 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", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Access Review Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475", + "AcrDelete": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "AcrImageSigner": "/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f", + "AcrPull": "/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d", + "AcrPush": "/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec", + "AcrQuarantineReader": "/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04", + "AcrQuarantineWriter": "/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608", + "AgFood Platform Sensor Partner Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67", + "AgFood Platform Service Admin": "/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3", + "AgFood Platform Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728", + "AgFood Platform Service Reader": "/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba", + "AnyBuild Builder": "/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8", + "API Management Developer Portal Content Editor": "/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729", + "API Management Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c", + "API Management Service Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61", + "API Management Service Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d", + "App Configuration Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b", + "App Configuration Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071", + "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", + "Application Insights Component Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e", + "Application Insights Snapshot Debugger": "/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b", + "Attestation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e", + "Attestation Reader": "/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3", + "Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867", + "Automation Job Operator": "/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f", + "Automation Operator": "/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404", + "Automation Runbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5", + "Autonomous Development Platform Data Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795", + "Autonomous Development Platform Data Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d", + "Autonomous Development Platform Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093", + "Avere Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a", + "Avere Operator": "/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9", + "Azure Arc Enabled Kubernetes Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd", + "Azure Arc Kubernetes Admin": "/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96", + "Azure Arc Kubernetes Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2", + "Azure Arc Kubernetes Viewer": "/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4", + "Azure Arc Kubernetes Writer": "/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1", + "Azure Arc ScVmm Administrator role": "/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87", + "Azure Arc ScVmm Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda", + "Azure Arc ScVmm Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9", + "Azure Arc ScVmm VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b", + "Azure Arc VMware Administrator role ": "/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f", + "Azure Arc VMware Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83", + "Azure Arc VMware Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa", + "Azure Arc VMware VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb", + "Azure Center for SAP solutions administrator": "/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7", + "Azure Center for SAP solutions Management role": "/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310", + "Azure Center for SAP solutions reader": "/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b", + "Azure Center for SAP solutions service role": "/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138", + "Azure Center for SAP solutions Service role for management": "/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb", + "Azure Connected Machine Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7", + "Azure Connected Machine Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302", + "Azure Connected Machine Resource Manager": "/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c", + "Azure Connected SQL Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508", + "Azure Digital Twins Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe", + "Azure Digital Twins Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3", + "Azure Event Hubs Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec", + "Azure Event Hubs Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde", + "Azure Event Hubs Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975", + "Azure Extension for SQL Server Deployment": "/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d", + "Azure Front Door Domain Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8", + "Azure Front Door Domain Reader": "/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f", + "Azure Front Door Secret Contributor": "/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5", + "Azure Front Door Secret Reader": "/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca", + "Azure Kubernetes Fleet Manager Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf", + "Azure Kubernetes Fleet Manager RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba", + "Azure Kubernetes Fleet Manager RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69", + "Azure Kubernetes Fleet Manager RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80", + "Azure Kubernetes Fleet Manager RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683", + "Azure Kubernetes Service Cluster Admin Role": "/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8", + "Azure Kubernetes Service Cluster Monitoring User": "/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6", + "Azure Kubernetes Service Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f", + "Azure Kubernetes Service Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8", + "Azure Kubernetes Service Policy Add-on Deployment": "/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064", + "Azure Kubernetes Service RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7", + "Azure Kubernetes Service RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b", + "Azure Kubernetes Service RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db", + "Azure Kubernetes Service RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb", + "Azure Maps Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb", + "Azure Maps Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204", + "Azure Maps Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa", + "Azure Maps Search and Render Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005", + "Azure Relay Listener": "/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d", + "Azure Relay Owner": "/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38", + "Azure Relay Sender": "/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d", + "Azure Service Bus Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419", + "Azure Service Bus Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0", + "Azure Service Bus Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39", + "Azure Spring Apps Connect Role": "/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b", + "Azure Spring Apps Remote Debugging Role": "/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054", + "Azure Spring Cloud Config Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b", + "Azure Spring Cloud Config Server Reader": "/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7", + "Azure Spring Cloud Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c", + "Azure Spring Cloud Service Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1", + "Azure Spring Cloud Service Registry Reader": "/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65", + "Azure Stack HCI registration role": "/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06", + "Azure Stack Registration Owner": "/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a", + "Azure Traffic Controller Configuration Manager": "/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1", + "Azure Usage Billing Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6", + "Azure VM Managed identities restore Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd", + "AzureML Compute Operator": "/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815", + "AzureML Data Scientist": "/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121", + "AzureML Metrics Writer (preview)": "/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae", + "AzureML Registry User": "/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615", + "Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b", + "Backup Operator": "/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324", + "Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912", + "Bayer Ag Powered Services CWUM Solution User Role": "/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa", + "Bayer Ag Powered Services GDU Solution": "/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042", + "Bayer Ag Powered Services Imagery Solution": "/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588", + "Billing Reader": "/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64", + "BizTalk Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342", + "Blockchain Member Node Access (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24", + "Blueprint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4", + "Blueprint Operator": "/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090", + "CDN Endpoint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45", + "CDN Endpoint Reader": "/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd", + "CDN Profile Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432", + "CDN Profile Reader": "/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af", + "Chamber Admin": "/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a", + "Chamber User": "/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32", + "Classic Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f", + "Classic Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25", + "Classic Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d", + "Classic Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb", + "ClearDB MySQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe", + "Code Signing Certificate Profile Signer": "/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958", + "Code Signing Identity Verifier": "/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08", + "Cognitive Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68", + "Cognitive Services Custom Vision Contributor": "/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3", + "Cognitive Services Custom Vision Deployment": "/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f", + "Cognitive Services Custom Vision Labeler": "/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c", + "Cognitive Services Custom Vision Reader": "/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73", + "Cognitive Services Custom Vision Trainer": "/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b", + "Cognitive Services Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c", + "Cognitive Services Face Recognizer": "/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7", + "Cognitive Services Immersive Reader User": "/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d", + "Cognitive Services Language Owner": "/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498", + "Cognitive Services Language Reader": "/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e", + "Cognitive Services Language Writer": "/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8", + "Cognitive Services LUIS Owner": "/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8", + "Cognitive Services LUIS Reader": "/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226", + "Cognitive Services LUIS Writer": "/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27", + "Cognitive Services Metrics Advisor Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a", + "Cognitive Services Metrics Advisor User": "/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8", + "Cognitive Services OpenAI Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442", + "Cognitive Services OpenAI User": "/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd", + "Cognitive Services QnA Maker Editor": "/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025", + "Cognitive Services QnA Maker Reader": "/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126", + "Cognitive Services Speech Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181", + "Cognitive Services Speech User": "/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447", + "Cognitive Services User": "/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908", + "Collaborative Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352", + "Collaborative Runtime Operator": "/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102", + "Compute Gallery Sharing Admin": "/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b", + "ContainerApp Reader": "/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b", + "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", + "Cosmos DB Account Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8", + "Cosmos DB Operator": "/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa", + "CosmosBackupOperator": "/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb", + "CosmosRestoreOperator": "/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f", + "Cost Management Contributor": "/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430", + "Cost Management Reader": "/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3", + "Data Box Contributor": "/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5", + "Data Box Reader": "/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027", + "Data Factory Contributor": "/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5", + "Data Labeling - Labeler": "/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab", + "Data Lake Analytics Developer": "/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88", + "Data Operator for Managed Disks": "/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e", + "Data Purger": "/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90", + "Deployment Environments User": "/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c", + "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", + "Desktop Virtualization Application Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55", + "Desktop Virtualization Contributor": "/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387", + "Desktop Virtualization Host Pool Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc", + "Desktop Virtualization Host Pool Reader": "/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822", + "Desktop Virtualization Power On Contributor": "/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33", + "Desktop Virtualization Power On Off Contributor": "/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e", + "Desktop Virtualization Reader": "/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868", + "Desktop Virtualization Session Host Operator": "/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408", + "Desktop Virtualization User": "/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63", + "Desktop Virtualization User Session Operator": "/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6", + "Desktop Virtualization Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c", + "Desktop Virtualization Workspace Contributor": "/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b", + "Desktop Virtualization Workspace Reader": "/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d", + "DevCenter Dev Box User": "/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05", + "DevCenter Project Admin": "/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0", + "Device Provisioning Service Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633", + "Device Provisioning Service Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8", + "Device Update Administrator": "/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a", + "Device Update Content Administrator": "/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98", + "Device Update Content Reader": "/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b", + "Device Update Deployments Administrator": "/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432", + "Device Update Deployments Reader": "/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f", + "Device Update Reader": "/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f", + "DevTest Labs User": "/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64", + "DICOM Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8", + "DICOM Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a", + "Disk Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24", + "Disk Pool Operator": "/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840", + "Disk Restore Operator": "/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13", + "Disk Snapshot Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce", + "DNS Resolver Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d", + "DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314", + "DocumentDB Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450", + "Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2", + "Domain Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb", + "Elastic SAN Owner": "/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406", + "Elastic SAN Reader": "/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca", + "Elastic SAN Volume Group Owner": "/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23", + "EventGrid Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de", + "EventGrid Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7", + "EventGrid EventSubscription Contributor": "/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443", + "EventGrid EventSubscription Reader": "/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405", + "Experimentation Administrator": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c", + "Experimentation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c", + "Experimentation Metric Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0", + "Experimentation Reader": "/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1", + "FHIR Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd", + "FHIR Data Converter": "/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24", + "FHIR Data Exporter": "/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843", + "FHIR Data Importer": "/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b", + "FHIR Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508", + "FHIR Data Writer": "/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913", + "FHIR SMART User": "/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0", + "Grafana Admin": "/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41", + "Grafana Editor": "/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f", + "Grafana Viewer": "/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769", + "Graph Owner": "/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9", + "Guest Configuration Resource Contributor": "/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31", + "HDInsight Cluster Operator": "/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a", + "HDInsight Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c", + "Hierarchy Settings Administrator": "/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d", + "Hybrid Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb", + "Hybrid Server Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624", + "Impact Reader": "/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e", + "Impact Reporter": "/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a", + "Integration Service Environment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8", + "Integration Service Environment Developer": "/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec", + "Intelligent Systems Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e", + "IoT Hub Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f", + "IoT Hub Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3", + "IoT Hub Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47", + "IoT Hub Twin Contributor": "/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c", + "Key Vault Administrator": "/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483", + "Key Vault Certificates Officer": "/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985", + "Key Vault Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395", + "Key Vault Crypto Officer": "/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603", + "Key Vault Crypto Service Encryption User": "/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6", + "Key Vault Crypto User": "/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424", + "Key Vault Reader": "/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2", + "Key Vault Secrets Officer": "/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7", + "Key Vault Secrets User": "/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6", + "Knowledge Consumer": "/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c", + "Kubernetes Agentless Operator": "/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6", + "Kubernetes Cluster - Azure Arc Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41", + "Kubernetes Extension Contributor": "/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717", + "Kubernetes Namespace User": "/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a", + "Lab Assistant": "/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1", + "Lab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270", + "Lab Creator": "/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead", + "Lab Operator": "/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d", + "Lab Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f", + "Lab Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc", + "Load Test Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e", + "Load Test Owner": "/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6", + "Load Test Reader": "/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081", + "LocalNGFirewallAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2", + "LocalRulestacksAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20", + "Log Analytics Contributor": "/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293", + "Log Analytics Reader": "/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893", + "Logic App Contributor": "/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e", + "Logic App Operator": "/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe", + "Managed Application Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e", + "Managed Application Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae", + "Managed Applications Reader": "/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44", + "Managed HSM contributor": "/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d", + "Managed Identity Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59", + "Managed Identity Operator": "/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830", + "Managed Services Registration assignment Delete Role": "/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46", + "Management Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c", + "Management Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d", + "Media Services Account Administrator": "/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466", + "Media Services Live Events Administrator": "/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77", + "Media Services Media Operator": "/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c", + "Media Services Policy Administrator": "/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae", + "Media Services Streaming Endpoints Administrator": "/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804", + "Microsoft Sentinel Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a", + "Microsoft Sentinel Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade", + "Microsoft Sentinel Playbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5", + "Microsoft Sentinel Reader": "/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb", + "Microsoft Sentinel Responder": "/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056", + "Microsoft.Kubernetes connected cluster role": "/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f", + "Monitoring Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa", + "Monitoring Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136", + "Monitoring Metrics Publisher": "/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb", + "Monitoring Reader": "/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05", + "MySQL Backup And Export Operator": "/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0", + "Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7", + "New Relic APM Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237", + "Object Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b", + "Object Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9", + "Object Understanding Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745", + "Object Understanding Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6", + "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", + "PlayFab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726", + "PlayFab Reader": "/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf", + "Policy Insights Data Writer (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84", + "Private DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f", + "Project Babylon Data Curator": "/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889", + "Project Babylon Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446", + "Project Babylon Data Source Administrator": "/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f", + "Purview role 1 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347", + "Purview role 2 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803", + "Purview role 3 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db", + "Quota Request Operator": "/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125", + "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", + "Reader and Data Access": "/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349", + "Redis Cache Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17", + "Remote Rendering Administrator": "/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e", + "Remote Rendering Client": "/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a", + "Reservation Purchaser": "/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689", + "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", + "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Scheduled Patching Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6", + "Scheduler Job Collections Contributor": "/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94", + "Schema Registry Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25", + "Schema Registry Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2", + "Search Index Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7", + "Search Index Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f", + "Search Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0", + "Security Admin": "/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd", + "Security Assessment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5", + "Security Detonation Chamber Publisher": "/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500", + "Security Detonation Chamber Reader": "/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5", + "Security Detonation Chamber Submission Manager": "/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce", + "Security Detonation Chamber Submitter": "/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0", + "Security Manager (Legacy)": "/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10", + "Security Reader": "/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4", + "Services Hub Operator": "/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b", + "SignalR AccessKey Reader": "/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e", + "SignalR App Server": "/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7", + "SignalR REST API Owner": "/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521", + "SignalR REST API Reader": "/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035", + "SignalR Service Owner": "/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3", + "SignalR/Web PubSub Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761", + "Site Recovery Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567", + "Site Recovery Operator": "/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca", + "Site Recovery Reader": "/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149", + "Spatial Anchors Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827", + "Spatial Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c", + "Spatial Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413", + "SQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec", + "SQL Managed Instance Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d", + "SQL Security Manager": "/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3", + "SQL Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437", + "SqlDb Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57", + "SqlMI Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872", + "SqlVM Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d", + "Storage Account Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1", + "Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab", + "Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12", + "Storage Blob Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe", + "Storage Blob Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b", + "Storage Blob Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1", + "Storage Blob Delegator": "/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a", + "Storage File Data SMB Share Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb", + "Storage File Data SMB Share Elevated Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7", + "Storage File Data SMB Share Reader": "/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314", + "Storage Queue Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88", + "Storage Queue Data Message Processor": "/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed", + "Storage Queue Data Message Sender": "/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a", + "Storage Queue Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925", + "Storage Table Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3", + "Storage Table Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6", + "Stream Analytics Query Tester": "/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf", + "Support Request Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e", + "Tag Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f", + "Template Spec Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b", + "Template Spec Reader": "/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e", + "Test Base Reader": "/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85", + "Traffic Manager Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7", + "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", + "Video Indexer Restricted Viewer": "/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb", + "Virtual Machine Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4", + "Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c", + "Virtual Machine Local User Login": "/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525", + "Virtual Machine User Login": "/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52", + "VM Scanner Operator": "/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd", + "Web Plan Contributor": "/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b", + "Web PubSub Service Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4", + "Web PubSub Service Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf", + "Website Contributor": "/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772", + "Windows Admin Center Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f", + "Workbook Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad", + "Workbook Reader": "/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d", + "WorkloadBuilder Migration Agent Role": "/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName')))]" + ] + }, + { + "condition": "[parameters('AllResourcesSameRG')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_DsktpRead_{0}', split(parameters('AVDResourceGroupId'), '/')[4])]", + "resourceGroup": "[split(parameters('AVDResourceGroupId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName'))), '2022-09-01').outputs.systemAssignedPrincipalId.value]" + }, + "roleDefinitionIdOrName": { + "value": "Desktop Virtualization Reader" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "[split(parameters('AVDResourceGroupId'), '/')[4]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "9545798095452579480" + } + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition, 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", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Access Review Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475", + "AcrDelete": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "AcrImageSigner": "/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f", + "AcrPull": "/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d", + "AcrPush": "/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec", + "AcrQuarantineReader": "/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04", + "AcrQuarantineWriter": "/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608", + "AgFood Platform Sensor Partner Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67", + "AgFood Platform Service Admin": "/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3", + "AgFood Platform Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728", + "AgFood Platform Service Reader": "/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba", + "AnyBuild Builder": "/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8", + "API Management Developer Portal Content Editor": "/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729", + "API Management Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c", + "API Management Service Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61", + "API Management Service Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d", + "App Configuration Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b", + "App Configuration Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071", + "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", + "Application Insights Component Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e", + "Application Insights Snapshot Debugger": "/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b", + "Attestation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e", + "Attestation Reader": "/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3", + "Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867", + "Automation Job Operator": "/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f", + "Automation Operator": "/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404", + "Automation Runbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5", + "Autonomous Development Platform Data Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795", + "Autonomous Development Platform Data Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d", + "Autonomous Development Platform Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093", + "Avere Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a", + "Avere Operator": "/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9", + "Azure Arc Enabled Kubernetes Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd", + "Azure Arc Kubernetes Admin": "/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96", + "Azure Arc Kubernetes Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2", + "Azure Arc Kubernetes Viewer": "/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4", + "Azure Arc Kubernetes Writer": "/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1", + "Azure Arc ScVmm Administrator role": "/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87", + "Azure Arc ScVmm Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda", + "Azure Arc ScVmm Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9", + "Azure Arc ScVmm VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b", + "Azure Arc VMware Administrator role ": "/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f", + "Azure Arc VMware Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83", + "Azure Arc VMware Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa", + "Azure Arc VMware VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb", + "Azure Center for SAP solutions administrator": "/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7", + "Azure Center for SAP solutions Management role": "/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310", + "Azure Center for SAP solutions reader": "/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b", + "Azure Center for SAP solutions service role": "/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138", + "Azure Center for SAP solutions Service role for management": "/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb", + "Azure Connected Machine Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7", + "Azure Connected Machine Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302", + "Azure Connected Machine Resource Manager": "/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c", + "Azure Connected SQL Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508", + "Azure Digital Twins Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe", + "Azure Digital Twins Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3", + "Azure Event Hubs Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec", + "Azure Event Hubs Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde", + "Azure Event Hubs Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975", + "Azure Extension for SQL Server Deployment": "/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d", + "Azure Front Door Domain Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8", + "Azure Front Door Domain Reader": "/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f", + "Azure Front Door Secret Contributor": "/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5", + "Azure Front Door Secret Reader": "/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca", + "Azure Kubernetes Fleet Manager Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf", + "Azure Kubernetes Fleet Manager RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba", + "Azure Kubernetes Fleet Manager RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69", + "Azure Kubernetes Fleet Manager RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80", + "Azure Kubernetes Fleet Manager RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683", + "Azure Kubernetes Service Cluster Admin Role": "/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8", + "Azure Kubernetes Service Cluster Monitoring User": "/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6", + "Azure Kubernetes Service Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f", + "Azure Kubernetes Service Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8", + "Azure Kubernetes Service Policy Add-on Deployment": "/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064", + "Azure Kubernetes Service RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7", + "Azure Kubernetes Service RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b", + "Azure Kubernetes Service RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db", + "Azure Kubernetes Service RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb", + "Azure Maps Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb", + "Azure Maps Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204", + "Azure Maps Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa", + "Azure Maps Search and Render Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005", + "Azure Relay Listener": "/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d", + "Azure Relay Owner": "/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38", + "Azure Relay Sender": "/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d", + "Azure Service Bus Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419", + "Azure Service Bus Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0", + "Azure Service Bus Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39", + "Azure Spring Apps Connect Role": "/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b", + "Azure Spring Apps Remote Debugging Role": "/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054", + "Azure Spring Cloud Config Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b", + "Azure Spring Cloud Config Server Reader": "/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7", + "Azure Spring Cloud Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c", + "Azure Spring Cloud Service Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1", + "Azure Spring Cloud Service Registry Reader": "/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65", + "Azure Stack HCI registration role": "/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06", + "Azure Stack Registration Owner": "/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a", + "Azure Traffic Controller Configuration Manager": "/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1", + "Azure Usage Billing Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6", + "Azure VM Managed identities restore Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd", + "AzureML Compute Operator": "/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815", + "AzureML Data Scientist": "/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121", + "AzureML Metrics Writer (preview)": "/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae", + "AzureML Registry User": "/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615", + "Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b", + "Backup Operator": "/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324", + "Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912", + "Bayer Ag Powered Services CWUM Solution User Role": "/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa", + "Bayer Ag Powered Services GDU Solution": "/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042", + "Bayer Ag Powered Services Imagery Solution": "/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588", + "Billing Reader": "/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64", + "BizTalk Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342", + "Blockchain Member Node Access (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24", + "Blueprint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4", + "Blueprint Operator": "/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090", + "CDN Endpoint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45", + "CDN Endpoint Reader": "/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd", + "CDN Profile Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432", + "CDN Profile Reader": "/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af", + "Chamber Admin": "/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a", + "Chamber User": "/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32", + "Classic Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f", + "Classic Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25", + "Classic Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d", + "Classic Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb", + "ClearDB MySQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe", + "Code Signing Certificate Profile Signer": "/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958", + "Code Signing Identity Verifier": "/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08", + "Cognitive Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68", + "Cognitive Services Custom Vision Contributor": "/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3", + "Cognitive Services Custom Vision Deployment": "/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f", + "Cognitive Services Custom Vision Labeler": "/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c", + "Cognitive Services Custom Vision Reader": "/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73", + "Cognitive Services Custom Vision Trainer": "/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b", + "Cognitive Services Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c", + "Cognitive Services Face Recognizer": "/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7", + "Cognitive Services Immersive Reader User": "/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d", + "Cognitive Services Language Owner": "/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498", + "Cognitive Services Language Reader": "/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e", + "Cognitive Services Language Writer": "/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8", + "Cognitive Services LUIS Owner": "/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8", + "Cognitive Services LUIS Reader": "/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226", + "Cognitive Services LUIS Writer": "/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27", + "Cognitive Services Metrics Advisor Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a", + "Cognitive Services Metrics Advisor User": "/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8", + "Cognitive Services OpenAI Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442", + "Cognitive Services OpenAI User": "/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd", + "Cognitive Services QnA Maker Editor": "/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025", + "Cognitive Services QnA Maker Reader": "/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126", + "Cognitive Services Speech Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181", + "Cognitive Services Speech User": "/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447", + "Cognitive Services User": "/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908", + "Collaborative Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352", + "Collaborative Runtime Operator": "/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102", + "Compute Gallery Sharing Admin": "/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b", + "ContainerApp Reader": "/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b", + "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", + "Cosmos DB Account Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8", + "Cosmos DB Operator": "/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa", + "CosmosBackupOperator": "/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb", + "CosmosRestoreOperator": "/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f", + "Cost Management Contributor": "/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430", + "Cost Management Reader": "/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3", + "Data Box Contributor": "/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5", + "Data Box Reader": "/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027", + "Data Factory Contributor": "/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5", + "Data Labeling - Labeler": "/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab", + "Data Lake Analytics Developer": "/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88", + "Data Operator for Managed Disks": "/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e", + "Data Purger": "/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90", + "Deployment Environments User": "/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c", + "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", + "Desktop Virtualization Application Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55", + "Desktop Virtualization Contributor": "/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387", + "Desktop Virtualization Host Pool Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc", + "Desktop Virtualization Host Pool Reader": "/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822", + "Desktop Virtualization Power On Contributor": "/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33", + "Desktop Virtualization Power On Off Contributor": "/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e", + "Desktop Virtualization Reader": "/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868", + "Desktop Virtualization Session Host Operator": "/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408", + "Desktop Virtualization User": "/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63", + "Desktop Virtualization User Session Operator": "/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6", + "Desktop Virtualization Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c", + "Desktop Virtualization Workspace Contributor": "/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b", + "Desktop Virtualization Workspace Reader": "/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d", + "DevCenter Dev Box User": "/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05", + "DevCenter Project Admin": "/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0", + "Device Provisioning Service Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633", + "Device Provisioning Service Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8", + "Device Update Administrator": "/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a", + "Device Update Content Administrator": "/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98", + "Device Update Content Reader": "/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b", + "Device Update Deployments Administrator": "/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432", + "Device Update Deployments Reader": "/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f", + "Device Update Reader": "/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f", + "DevTest Labs User": "/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64", + "DICOM Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8", + "DICOM Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a", + "Disk Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24", + "Disk Pool Operator": "/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840", + "Disk Restore Operator": "/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13", + "Disk Snapshot Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce", + "DNS Resolver Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d", + "DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314", + "DocumentDB Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450", + "Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2", + "Domain Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb", + "Elastic SAN Owner": "/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406", + "Elastic SAN Reader": "/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca", + "Elastic SAN Volume Group Owner": "/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23", + "EventGrid Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de", + "EventGrid Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7", + "EventGrid EventSubscription Contributor": "/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443", + "EventGrid EventSubscription Reader": "/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405", + "Experimentation Administrator": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c", + "Experimentation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c", + "Experimentation Metric Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0", + "Experimentation Reader": "/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1", + "FHIR Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd", + "FHIR Data Converter": "/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24", + "FHIR Data Exporter": "/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843", + "FHIR Data Importer": "/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b", + "FHIR Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508", + "FHIR Data Writer": "/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913", + "FHIR SMART User": "/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0", + "Grafana Admin": "/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41", + "Grafana Editor": "/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f", + "Grafana Viewer": "/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769", + "Graph Owner": "/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9", + "Guest Configuration Resource Contributor": "/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31", + "HDInsight Cluster Operator": "/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a", + "HDInsight Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c", + "Hierarchy Settings Administrator": "/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d", + "Hybrid Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb", + "Hybrid Server Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624", + "Impact Reader": "/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e", + "Impact Reporter": "/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a", + "Integration Service Environment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8", + "Integration Service Environment Developer": "/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec", + "Intelligent Systems Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e", + "IoT Hub Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f", + "IoT Hub Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3", + "IoT Hub Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47", + "IoT Hub Twin Contributor": "/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c", + "Key Vault Administrator": "/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483", + "Key Vault Certificates Officer": "/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985", + "Key Vault Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395", + "Key Vault Crypto Officer": "/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603", + "Key Vault Crypto Service Encryption User": "/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6", + "Key Vault Crypto User": "/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424", + "Key Vault Reader": "/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2", + "Key Vault Secrets Officer": "/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7", + "Key Vault Secrets User": "/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6", + "Knowledge Consumer": "/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c", + "Kubernetes Agentless Operator": "/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6", + "Kubernetes Cluster - Azure Arc Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41", + "Kubernetes Extension Contributor": "/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717", + "Kubernetes Namespace User": "/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a", + "Lab Assistant": "/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1", + "Lab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270", + "Lab Creator": "/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead", + "Lab Operator": "/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d", + "Lab Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f", + "Lab Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc", + "Load Test Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e", + "Load Test Owner": "/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6", + "Load Test Reader": "/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081", + "LocalNGFirewallAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2", + "LocalRulestacksAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20", + "Log Analytics Contributor": "/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293", + "Log Analytics Reader": "/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893", + "Logic App Contributor": "/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e", + "Logic App Operator": "/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe", + "Managed Application Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e", + "Managed Application Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae", + "Managed Applications Reader": "/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44", + "Managed HSM contributor": "/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d", + "Managed Identity Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59", + "Managed Identity Operator": "/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830", + "Managed Services Registration assignment Delete Role": "/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46", + "Management Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c", + "Management Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d", + "Media Services Account Administrator": "/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466", + "Media Services Live Events Administrator": "/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77", + "Media Services Media Operator": "/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c", + "Media Services Policy Administrator": "/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae", + "Media Services Streaming Endpoints Administrator": "/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804", + "Microsoft Sentinel Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a", + "Microsoft Sentinel Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade", + "Microsoft Sentinel Playbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5", + "Microsoft Sentinel Reader": "/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb", + "Microsoft Sentinel Responder": "/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056", + "Microsoft.Kubernetes connected cluster role": "/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f", + "Monitoring Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa", + "Monitoring Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136", + "Monitoring Metrics Publisher": "/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb", + "Monitoring Reader": "/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05", + "MySQL Backup And Export Operator": "/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0", + "Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7", + "New Relic APM Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237", + "Object Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b", + "Object Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9", + "Object Understanding Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745", + "Object Understanding Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6", + "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", + "PlayFab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726", + "PlayFab Reader": "/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf", + "Policy Insights Data Writer (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84", + "Private DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f", + "Project Babylon Data Curator": "/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889", + "Project Babylon Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446", + "Project Babylon Data Source Administrator": "/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f", + "Purview role 1 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347", + "Purview role 2 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803", + "Purview role 3 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db", + "Quota Request Operator": "/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125", + "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", + "Reader and Data Access": "/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349", + "Redis Cache Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17", + "Remote Rendering Administrator": "/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e", + "Remote Rendering Client": "/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a", + "Reservation Purchaser": "/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689", + "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", + "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Scheduled Patching Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6", + "Scheduler Job Collections Contributor": "/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94", + "Schema Registry Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25", + "Schema Registry Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2", + "Search Index Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7", + "Search Index Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f", + "Search Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0", + "Security Admin": "/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd", + "Security Assessment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5", + "Security Detonation Chamber Publisher": "/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500", + "Security Detonation Chamber Reader": "/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5", + "Security Detonation Chamber Submission Manager": "/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce", + "Security Detonation Chamber Submitter": "/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0", + "Security Manager (Legacy)": "/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10", + "Security Reader": "/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4", + "Services Hub Operator": "/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b", + "SignalR AccessKey Reader": "/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e", + "SignalR App Server": "/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7", + "SignalR REST API Owner": "/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521", + "SignalR REST API Reader": "/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035", + "SignalR Service Owner": "/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3", + "SignalR/Web PubSub Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761", + "Site Recovery Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567", + "Site Recovery Operator": "/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca", + "Site Recovery Reader": "/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149", + "Spatial Anchors Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827", + "Spatial Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c", + "Spatial Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413", + "SQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec", + "SQL Managed Instance Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d", + "SQL Security Manager": "/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3", + "SQL Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437", + "SqlDb Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57", + "SqlMI Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872", + "SqlVM Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d", + "Storage Account Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1", + "Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab", + "Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12", + "Storage Blob Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe", + "Storage Blob Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b", + "Storage Blob Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1", + "Storage Blob Delegator": "/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a", + "Storage File Data SMB Share Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb", + "Storage File Data SMB Share Elevated Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7", + "Storage File Data SMB Share Reader": "/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314", + "Storage Queue Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88", + "Storage Queue Data Message Processor": "/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed", + "Storage Queue Data Message Sender": "/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a", + "Storage Queue Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925", + "Storage Table Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3", + "Storage Table Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6", + "Stream Analytics Query Tester": "/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf", + "Support Request Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e", + "Tag Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f", + "Template Spec Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b", + "Template Spec Reader": "/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e", + "Test Base Reader": "/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85", + "Traffic Manager Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7", + "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", + "Video Indexer Restricted Viewer": "/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb", + "Virtual Machine Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4", + "Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c", + "Virtual Machine Local User Login": "/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525", + "Virtual Machine User Login": "/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52", + "VM Scanner Operator": "/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd", + "Web Plan Contributor": "/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b", + "Web PubSub Service Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4", + "Web PubSub Service Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf", + "Website Contributor": "/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772", + "Windows Admin Center Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f", + "Workbook Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad", + "Workbook Reader": "/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d", + "WorkloadBuilder Migration Agent Role": "/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_LogContrib_{0}', split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[4])]", + "subscriptionId": "[split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName'))), '2022-09-01').outputs.systemAssignedPrincipalId.value]" + }, + "roleDefinitionIdOrName": { + "value": "[format('/providers/Microsoft.Authorization/roleDefinitions/{0}', variables('RoleAssignments').LogAnalyticsContributor.GUID)]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "[split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[4]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "9545798095452579480" + } + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition, 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", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Access Review Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475", + "AcrDelete": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "AcrImageSigner": "/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f", + "AcrPull": "/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d", + "AcrPush": "/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec", + "AcrQuarantineReader": "/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04", + "AcrQuarantineWriter": "/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608", + "AgFood Platform Sensor Partner Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67", + "AgFood Platform Service Admin": "/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3", + "AgFood Platform Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728", + "AgFood Platform Service Reader": "/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba", + "AnyBuild Builder": "/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8", + "API Management Developer Portal Content Editor": "/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729", + "API Management Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c", + "API Management Service Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61", + "API Management Service Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d", + "App Configuration Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b", + "App Configuration Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071", + "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", + "Application Insights Component Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e", + "Application Insights Snapshot Debugger": "/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b", + "Attestation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e", + "Attestation Reader": "/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3", + "Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867", + "Automation Job Operator": "/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f", + "Automation Operator": "/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404", + "Automation Runbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5", + "Autonomous Development Platform Data Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795", + "Autonomous Development Platform Data Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d", + "Autonomous Development Platform Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093", + "Avere Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a", + "Avere Operator": "/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9", + "Azure Arc Enabled Kubernetes Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd", + "Azure Arc Kubernetes Admin": "/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96", + "Azure Arc Kubernetes Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2", + "Azure Arc Kubernetes Viewer": "/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4", + "Azure Arc Kubernetes Writer": "/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1", + "Azure Arc ScVmm Administrator role": "/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87", + "Azure Arc ScVmm Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda", + "Azure Arc ScVmm Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9", + "Azure Arc ScVmm VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b", + "Azure Arc VMware Administrator role ": "/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f", + "Azure Arc VMware Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83", + "Azure Arc VMware Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa", + "Azure Arc VMware VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb", + "Azure Center for SAP solutions administrator": "/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7", + "Azure Center for SAP solutions Management role": "/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310", + "Azure Center for SAP solutions reader": "/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b", + "Azure Center for SAP solutions service role": "/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138", + "Azure Center for SAP solutions Service role for management": "/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb", + "Azure Connected Machine Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7", + "Azure Connected Machine Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302", + "Azure Connected Machine Resource Manager": "/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c", + "Azure Connected SQL Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508", + "Azure Digital Twins Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe", + "Azure Digital Twins Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3", + "Azure Event Hubs Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec", + "Azure Event Hubs Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde", + "Azure Event Hubs Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975", + "Azure Extension for SQL Server Deployment": "/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d", + "Azure Front Door Domain Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8", + "Azure Front Door Domain Reader": "/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f", + "Azure Front Door Secret Contributor": "/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5", + "Azure Front Door Secret Reader": "/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca", + "Azure Kubernetes Fleet Manager Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf", + "Azure Kubernetes Fleet Manager RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba", + "Azure Kubernetes Fleet Manager RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69", + "Azure Kubernetes Fleet Manager RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80", + "Azure Kubernetes Fleet Manager RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683", + "Azure Kubernetes Service Cluster Admin Role": "/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8", + "Azure Kubernetes Service Cluster Monitoring User": "/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6", + "Azure Kubernetes Service Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f", + "Azure Kubernetes Service Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8", + "Azure Kubernetes Service Policy Add-on Deployment": "/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064", + "Azure Kubernetes Service RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7", + "Azure Kubernetes Service RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b", + "Azure Kubernetes Service RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db", + "Azure Kubernetes Service RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb", + "Azure Maps Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb", + "Azure Maps Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204", + "Azure Maps Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa", + "Azure Maps Search and Render Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005", + "Azure Relay Listener": "/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d", + "Azure Relay Owner": "/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38", + "Azure Relay Sender": "/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d", + "Azure Service Bus Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419", + "Azure Service Bus Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0", + "Azure Service Bus Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39", + "Azure Spring Apps Connect Role": "/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b", + "Azure Spring Apps Remote Debugging Role": "/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054", + "Azure Spring Cloud Config Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b", + "Azure Spring Cloud Config Server Reader": "/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7", + "Azure Spring Cloud Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c", + "Azure Spring Cloud Service Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1", + "Azure Spring Cloud Service Registry Reader": "/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65", + "Azure Stack HCI registration role": "/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06", + "Azure Stack Registration Owner": "/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a", + "Azure Traffic Controller Configuration Manager": "/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1", + "Azure Usage Billing Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6", + "Azure VM Managed identities restore Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd", + "AzureML Compute Operator": "/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815", + "AzureML Data Scientist": "/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121", + "AzureML Metrics Writer (preview)": "/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae", + "AzureML Registry User": "/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615", + "Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b", + "Backup Operator": "/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324", + "Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912", + "Bayer Ag Powered Services CWUM Solution User Role": "/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa", + "Bayer Ag Powered Services GDU Solution": "/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042", + "Bayer Ag Powered Services Imagery Solution": "/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588", + "Billing Reader": "/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64", + "BizTalk Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342", + "Blockchain Member Node Access (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24", + "Blueprint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4", + "Blueprint Operator": "/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090", + "CDN Endpoint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45", + "CDN Endpoint Reader": "/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd", + "CDN Profile Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432", + "CDN Profile Reader": "/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af", + "Chamber Admin": "/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a", + "Chamber User": "/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32", + "Classic Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f", + "Classic Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25", + "Classic Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d", + "Classic Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb", + "ClearDB MySQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe", + "Code Signing Certificate Profile Signer": "/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958", + "Code Signing Identity Verifier": "/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08", + "Cognitive Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68", + "Cognitive Services Custom Vision Contributor": "/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3", + "Cognitive Services Custom Vision Deployment": "/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f", + "Cognitive Services Custom Vision Labeler": "/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c", + "Cognitive Services Custom Vision Reader": "/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73", + "Cognitive Services Custom Vision Trainer": "/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b", + "Cognitive Services Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c", + "Cognitive Services Face Recognizer": "/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7", + "Cognitive Services Immersive Reader User": "/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d", + "Cognitive Services Language Owner": "/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498", + "Cognitive Services Language Reader": "/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e", + "Cognitive Services Language Writer": "/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8", + "Cognitive Services LUIS Owner": "/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8", + "Cognitive Services LUIS Reader": "/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226", + "Cognitive Services LUIS Writer": "/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27", + "Cognitive Services Metrics Advisor Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a", + "Cognitive Services Metrics Advisor User": "/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8", + "Cognitive Services OpenAI Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442", + "Cognitive Services OpenAI User": "/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd", + "Cognitive Services QnA Maker Editor": "/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025", + "Cognitive Services QnA Maker Reader": "/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126", + "Cognitive Services Speech Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181", + "Cognitive Services Speech User": "/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447", + "Cognitive Services User": "/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908", + "Collaborative Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352", + "Collaborative Runtime Operator": "/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102", + "Compute Gallery Sharing Admin": "/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b", + "ContainerApp Reader": "/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b", + "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", + "Cosmos DB Account Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8", + "Cosmos DB Operator": "/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa", + "CosmosBackupOperator": "/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb", + "CosmosRestoreOperator": "/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f", + "Cost Management Contributor": "/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430", + "Cost Management Reader": "/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3", + "Data Box Contributor": "/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5", + "Data Box Reader": "/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027", + "Data Factory Contributor": "/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5", + "Data Labeling - Labeler": "/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab", + "Data Lake Analytics Developer": "/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88", + "Data Operator for Managed Disks": "/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e", + "Data Purger": "/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90", + "Deployment Environments User": "/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c", + "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", + "Desktop Virtualization Application Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55", + "Desktop Virtualization Contributor": "/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387", + "Desktop Virtualization Host Pool Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc", + "Desktop Virtualization Host Pool Reader": "/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822", + "Desktop Virtualization Power On Contributor": "/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33", + "Desktop Virtualization Power On Off Contributor": "/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e", + "Desktop Virtualization Reader": "/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868", + "Desktop Virtualization Session Host Operator": "/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408", + "Desktop Virtualization User": "/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63", + "Desktop Virtualization User Session Operator": "/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6", + "Desktop Virtualization Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c", + "Desktop Virtualization Workspace Contributor": "/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b", + "Desktop Virtualization Workspace Reader": "/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d", + "DevCenter Dev Box User": "/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05", + "DevCenter Project Admin": "/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0", + "Device Provisioning Service Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633", + "Device Provisioning Service Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8", + "Device Update Administrator": "/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a", + "Device Update Content Administrator": "/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98", + "Device Update Content Reader": "/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b", + "Device Update Deployments Administrator": "/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432", + "Device Update Deployments Reader": "/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f", + "Device Update Reader": "/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f", + "DevTest Labs User": "/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64", + "DICOM Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8", + "DICOM Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a", + "Disk Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24", + "Disk Pool Operator": "/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840", + "Disk Restore Operator": "/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13", + "Disk Snapshot Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce", + "DNS Resolver Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d", + "DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314", + "DocumentDB Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450", + "Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2", + "Domain Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb", + "Elastic SAN Owner": "/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406", + "Elastic SAN Reader": "/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca", + "Elastic SAN Volume Group Owner": "/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23", + "EventGrid Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de", + "EventGrid Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7", + "EventGrid EventSubscription Contributor": "/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443", + "EventGrid EventSubscription Reader": "/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405", + "Experimentation Administrator": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c", + "Experimentation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c", + "Experimentation Metric Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0", + "Experimentation Reader": "/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1", + "FHIR Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd", + "FHIR Data Converter": "/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24", + "FHIR Data Exporter": "/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843", + "FHIR Data Importer": "/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b", + "FHIR Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508", + "FHIR Data Writer": "/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913", + "FHIR SMART User": "/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0", + "Grafana Admin": "/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41", + "Grafana Editor": "/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f", + "Grafana Viewer": "/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769", + "Graph Owner": "/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9", + "Guest Configuration Resource Contributor": "/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31", + "HDInsight Cluster Operator": "/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a", + "HDInsight Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c", + "Hierarchy Settings Administrator": "/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d", + "Hybrid Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb", + "Hybrid Server Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624", + "Impact Reader": "/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e", + "Impact Reporter": "/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a", + "Integration Service Environment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8", + "Integration Service Environment Developer": "/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec", + "Intelligent Systems Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e", + "IoT Hub Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f", + "IoT Hub Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3", + "IoT Hub Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47", + "IoT Hub Twin Contributor": "/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c", + "Key Vault Administrator": "/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483", + "Key Vault Certificates Officer": "/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985", + "Key Vault Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395", + "Key Vault Crypto Officer": "/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603", + "Key Vault Crypto Service Encryption User": "/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6", + "Key Vault Crypto User": "/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424", + "Key Vault Reader": "/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2", + "Key Vault Secrets Officer": "/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7", + "Key Vault Secrets User": "/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6", + "Knowledge Consumer": "/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c", + "Kubernetes Agentless Operator": "/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6", + "Kubernetes Cluster - Azure Arc Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41", + "Kubernetes Extension Contributor": "/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717", + "Kubernetes Namespace User": "/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a", + "Lab Assistant": "/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1", + "Lab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270", + "Lab Creator": "/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead", + "Lab Operator": "/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d", + "Lab Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f", + "Lab Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc", + "Load Test Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e", + "Load Test Owner": "/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6", + "Load Test Reader": "/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081", + "LocalNGFirewallAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2", + "LocalRulestacksAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20", + "Log Analytics Contributor": "/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293", + "Log Analytics Reader": "/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893", + "Logic App Contributor": "/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e", + "Logic App Operator": "/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe", + "Managed Application Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e", + "Managed Application Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae", + "Managed Applications Reader": "/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44", + "Managed HSM contributor": "/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d", + "Managed Identity Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59", + "Managed Identity Operator": "/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830", + "Managed Services Registration assignment Delete Role": "/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46", + "Management Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c", + "Management Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d", + "Media Services Account Administrator": "/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466", + "Media Services Live Events Administrator": "/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77", + "Media Services Media Operator": "/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c", + "Media Services Policy Administrator": "/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae", + "Media Services Streaming Endpoints Administrator": "/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804", + "Microsoft Sentinel Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a", + "Microsoft Sentinel Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade", + "Microsoft Sentinel Playbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5", + "Microsoft Sentinel Reader": "/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb", + "Microsoft Sentinel Responder": "/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056", + "Microsoft.Kubernetes connected cluster role": "/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f", + "Monitoring Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa", + "Monitoring Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136", + "Monitoring Metrics Publisher": "/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb", + "Monitoring Reader": "/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05", + "MySQL Backup And Export Operator": "/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0", + "Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7", + "New Relic APM Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237", + "Object Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b", + "Object Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9", + "Object Understanding Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745", + "Object Understanding Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6", + "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", + "PlayFab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726", + "PlayFab Reader": "/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf", + "Policy Insights Data Writer (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84", + "Private DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f", + "Project Babylon Data Curator": "/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889", + "Project Babylon Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446", + "Project Babylon Data Source Administrator": "/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f", + "Purview role 1 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347", + "Purview role 2 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803", + "Purview role 3 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db", + "Quota Request Operator": "/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125", + "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", + "Reader and Data Access": "/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349", + "Redis Cache Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17", + "Remote Rendering Administrator": "/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e", + "Remote Rendering Client": "/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a", + "Reservation Purchaser": "/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689", + "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", + "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Scheduled Patching Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6", + "Scheduler Job Collections Contributor": "/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94", + "Schema Registry Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25", + "Schema Registry Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2", + "Search Index Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7", + "Search Index Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f", + "Search Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0", + "Security Admin": "/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd", + "Security Assessment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5", + "Security Detonation Chamber Publisher": "/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500", + "Security Detonation Chamber Reader": "/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5", + "Security Detonation Chamber Submission Manager": "/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce", + "Security Detonation Chamber Submitter": "/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0", + "Security Manager (Legacy)": "/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10", + "Security Reader": "/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4", + "Services Hub Operator": "/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b", + "SignalR AccessKey Reader": "/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e", + "SignalR App Server": "/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7", + "SignalR REST API Owner": "/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521", + "SignalR REST API Reader": "/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035", + "SignalR Service Owner": "/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3", + "SignalR/Web PubSub Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761", + "Site Recovery Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567", + "Site Recovery Operator": "/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca", + "Site Recovery Reader": "/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149", + "Spatial Anchors Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827", + "Spatial Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c", + "Spatial Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413", + "SQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec", + "SQL Managed Instance Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d", + "SQL Security Manager": "/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3", + "SQL Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437", + "SqlDb Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57", + "SqlMI Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872", + "SqlVM Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d", + "Storage Account Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1", + "Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab", + "Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12", + "Storage Blob Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe", + "Storage Blob Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b", + "Storage Blob Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1", + "Storage Blob Delegator": "/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a", + "Storage File Data SMB Share Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb", + "Storage File Data SMB Share Elevated Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7", + "Storage File Data SMB Share Reader": "/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314", + "Storage Queue Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88", + "Storage Queue Data Message Processor": "/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed", + "Storage Queue Data Message Sender": "/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a", + "Storage Queue Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925", + "Storage Table Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3", + "Storage Table Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6", + "Stream Analytics Query Tester": "/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf", + "Support Request Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e", + "Tag Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f", + "Template Spec Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b", + "Template Spec Reader": "/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e", + "Test Base Reader": "/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85", + "Traffic Manager Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7", + "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", + "Video Indexer Restricted Viewer": "/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb", + "Virtual Machine Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4", + "Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c", + "Virtual Machine Local User Login": "/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525", + "Virtual Machine User Login": "/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52", + "VM Scanner Operator": "/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd", + "Web Plan Contributor": "/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b", + "Web PubSub Service Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4", + "Web PubSub Service Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf", + "Website Contributor": "/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772", + "Windows Admin Center Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f", + "Workbook Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad", + "Workbook Reader": "/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d", + "WorkloadBuilder Migration Agent Role": "/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName')))]" + ] + }, + { + "copy": { + "name": "roleAssignment_Storage", + "count": "[length(variables('StorAcctRGs'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_StorAcctContrib_{0}', variables('StorAcctRGs')[copyIndex()])]", + "resourceGroup": "[variables('StorAcctRGs')[copyIndex()]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName'))), '2022-09-01').outputs.systemAssignedPrincipalId.value]" + }, + "roleDefinitionIdOrName": { + "value": "[format('/providers/Microsoft.Authorization/roleDefinitions/{0}', variables('RoleAssignments').StoreAcctContrib.GUID)]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "[variables('StorAcctRGs')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "9545798095452579480" + } + }, + "parameters": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. You can provide either the display name of the role definition, 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", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "builtInRoleNames": { + "Access Review Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475", + "AcrDelete": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "AcrImageSigner": "/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f", + "AcrPull": "/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d", + "AcrPush": "/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec", + "AcrQuarantineReader": "/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04", + "AcrQuarantineWriter": "/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608", + "AgFood Platform Sensor Partner Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67", + "AgFood Platform Service Admin": "/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3", + "AgFood Platform Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728", + "AgFood Platform Service Reader": "/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba", + "AnyBuild Builder": "/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8", + "API Management Developer Portal Content Editor": "/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729", + "API Management Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c", + "API Management Service Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61", + "API Management Service Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d", + "App Configuration Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b", + "App Configuration Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071", + "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", + "Application Insights Component Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e", + "Application Insights Snapshot Debugger": "/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b", + "Attestation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e", + "Attestation Reader": "/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3", + "Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867", + "Automation Job Operator": "/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f", + "Automation Operator": "/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404", + "Automation Runbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5", + "Autonomous Development Platform Data Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795", + "Autonomous Development Platform Data Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d", + "Autonomous Development Platform Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093", + "Avere Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a", + "Avere Operator": "/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9", + "Azure Arc Enabled Kubernetes Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd", + "Azure Arc Kubernetes Admin": "/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96", + "Azure Arc Kubernetes Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2", + "Azure Arc Kubernetes Viewer": "/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4", + "Azure Arc Kubernetes Writer": "/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1", + "Azure Arc ScVmm Administrator role": "/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87", + "Azure Arc ScVmm Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda", + "Azure Arc ScVmm Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9", + "Azure Arc ScVmm VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b", + "Azure Arc VMware Administrator role ": "/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f", + "Azure Arc VMware Private Cloud User": "/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83", + "Azure Arc VMware Private Clouds Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa", + "Azure Arc VMware VM Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb", + "Azure Center for SAP solutions administrator": "/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7", + "Azure Center for SAP solutions Management role": "/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310", + "Azure Center for SAP solutions reader": "/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b", + "Azure Center for SAP solutions service role": "/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138", + "Azure Center for SAP solutions Service role for management": "/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb", + "Azure Connected Machine Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7", + "Azure Connected Machine Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302", + "Azure Connected Machine Resource Manager": "/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c", + "Azure Connected SQL Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508", + "Azure Digital Twins Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe", + "Azure Digital Twins Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3", + "Azure Event Hubs Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec", + "Azure Event Hubs Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde", + "Azure Event Hubs Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975", + "Azure Extension for SQL Server Deployment": "/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d", + "Azure Front Door Domain Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8", + "Azure Front Door Domain Reader": "/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f", + "Azure Front Door Secret Contributor": "/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5", + "Azure Front Door Secret Reader": "/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca", + "Azure Kubernetes Fleet Manager Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf", + "Azure Kubernetes Fleet Manager RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba", + "Azure Kubernetes Fleet Manager RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69", + "Azure Kubernetes Fleet Manager RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80", + "Azure Kubernetes Fleet Manager RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683", + "Azure Kubernetes Service Cluster Admin Role": "/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8", + "Azure Kubernetes Service Cluster Monitoring User": "/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6", + "Azure Kubernetes Service Cluster User Role": "/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f", + "Azure Kubernetes Service Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8", + "Azure Kubernetes Service Policy Add-on Deployment": "/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064", + "Azure Kubernetes Service RBAC Admin": "/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7", + "Azure Kubernetes Service RBAC Cluster Admin": "/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b", + "Azure Kubernetes Service RBAC Reader": "/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db", + "Azure Kubernetes Service RBAC Writer": "/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb", + "Azure Maps Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb", + "Azure Maps Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204", + "Azure Maps Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa", + "Azure Maps Search and Render Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005", + "Azure Relay Listener": "/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d", + "Azure Relay Owner": "/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38", + "Azure Relay Sender": "/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d", + "Azure Service Bus Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419", + "Azure Service Bus Data Receiver": "/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0", + "Azure Service Bus Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39", + "Azure Spring Apps Connect Role": "/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b", + "Azure Spring Apps Remote Debugging Role": "/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054", + "Azure Spring Cloud Config Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b", + "Azure Spring Cloud Config Server Reader": "/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7", + "Azure Spring Cloud Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c", + "Azure Spring Cloud Service Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1", + "Azure Spring Cloud Service Registry Reader": "/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65", + "Azure Stack HCI registration role": "/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06", + "Azure Stack Registration Owner": "/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a", + "Azure Traffic Controller Configuration Manager": "/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1", + "Azure Usage Billing Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6", + "Azure VM Managed identities restore Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd", + "AzureML Compute Operator": "/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815", + "AzureML Data Scientist": "/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121", + "AzureML Metrics Writer (preview)": "/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae", + "AzureML Registry User": "/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615", + "Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b", + "Backup Operator": "/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324", + "Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912", + "Bayer Ag Powered Services CWUM Solution User Role": "/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa", + "Bayer Ag Powered Services GDU Solution": "/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042", + "Bayer Ag Powered Services Imagery Solution": "/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588", + "Billing Reader": "/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64", + "BizTalk Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342", + "Blockchain Member Node Access (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24", + "Blueprint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4", + "Blueprint Operator": "/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090", + "CDN Endpoint Contributor": "/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45", + "CDN Endpoint Reader": "/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd", + "CDN Profile Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432", + "CDN Profile Reader": "/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af", + "Chamber Admin": "/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a", + "Chamber User": "/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32", + "Classic Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f", + "Classic Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25", + "Classic Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d", + "Classic Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb", + "ClearDB MySQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe", + "Code Signing Certificate Profile Signer": "/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958", + "Code Signing Identity Verifier": "/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08", + "Cognitive Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68", + "Cognitive Services Custom Vision Contributor": "/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3", + "Cognitive Services Custom Vision Deployment": "/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f", + "Cognitive Services Custom Vision Labeler": "/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c", + "Cognitive Services Custom Vision Reader": "/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73", + "Cognitive Services Custom Vision Trainer": "/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b", + "Cognitive Services Data Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c", + "Cognitive Services Face Recognizer": "/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7", + "Cognitive Services Immersive Reader User": "/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d", + "Cognitive Services Language Owner": "/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498", + "Cognitive Services Language Reader": "/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e", + "Cognitive Services Language Writer": "/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8", + "Cognitive Services LUIS Owner": "/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8", + "Cognitive Services LUIS Reader": "/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226", + "Cognitive Services LUIS Writer": "/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27", + "Cognitive Services Metrics Advisor Administrator": "/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a", + "Cognitive Services Metrics Advisor User": "/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8", + "Cognitive Services OpenAI Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442", + "Cognitive Services OpenAI User": "/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd", + "Cognitive Services QnA Maker Editor": "/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025", + "Cognitive Services QnA Maker Reader": "/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126", + "Cognitive Services Speech Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181", + "Cognitive Services Speech User": "/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447", + "Cognitive Services User": "/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908", + "Collaborative Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352", + "Collaborative Runtime Operator": "/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102", + "Compute Gallery Sharing Admin": "/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b", + "ContainerApp Reader": "/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b", + "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", + "Cosmos DB Account Reader Role": "/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8", + "Cosmos DB Operator": "/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa", + "CosmosBackupOperator": "/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb", + "CosmosRestoreOperator": "/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f", + "Cost Management Contributor": "/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430", + "Cost Management Reader": "/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3", + "Data Box Contributor": "/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5", + "Data Box Reader": "/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027", + "Data Factory Contributor": "/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5", + "Data Labeling - Labeler": "/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab", + "Data Lake Analytics Developer": "/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88", + "Data Operator for Managed Disks": "/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e", + "Data Purger": "/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90", + "Deployment Environments User": "/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c", + "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", + "Desktop Virtualization Application Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55", + "Desktop Virtualization Contributor": "/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387", + "Desktop Virtualization Host Pool Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc", + "Desktop Virtualization Host Pool Reader": "/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822", + "Desktop Virtualization Power On Contributor": "/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33", + "Desktop Virtualization Power On Off Contributor": "/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e", + "Desktop Virtualization Reader": "/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868", + "Desktop Virtualization Session Host Operator": "/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408", + "Desktop Virtualization User": "/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63", + "Desktop Virtualization User Session Operator": "/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6", + "Desktop Virtualization Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c", + "Desktop Virtualization Workspace Contributor": "/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b", + "Desktop Virtualization Workspace Reader": "/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d", + "DevCenter Dev Box User": "/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05", + "DevCenter Project Admin": "/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0", + "Device Provisioning Service Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633", + "Device Provisioning Service Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8", + "Device Update Administrator": "/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a", + "Device Update Content Administrator": "/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98", + "Device Update Content Reader": "/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b", + "Device Update Deployments Administrator": "/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432", + "Device Update Deployments Reader": "/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f", + "Device Update Reader": "/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f", + "DevTest Labs User": "/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64", + "DICOM Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8", + "DICOM Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a", + "Disk Backup Reader": "/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24", + "Disk Pool Operator": "/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840", + "Disk Restore Operator": "/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13", + "Disk Snapshot Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce", + "DNS Resolver Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d", + "DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314", + "DocumentDB Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450", + "Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2", + "Domain Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb", + "Elastic SAN Owner": "/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406", + "Elastic SAN Reader": "/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca", + "Elastic SAN Volume Group Owner": "/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23", + "EventGrid Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de", + "EventGrid Data Sender": "/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7", + "EventGrid EventSubscription Contributor": "/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443", + "EventGrid EventSubscription Reader": "/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405", + "Experimentation Administrator": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c", + "Experimentation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c", + "Experimentation Metric Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0", + "Experimentation Reader": "/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1", + "FHIR Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd", + "FHIR Data Converter": "/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24", + "FHIR Data Exporter": "/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843", + "FHIR Data Importer": "/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b", + "FHIR Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508", + "FHIR Data Writer": "/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913", + "FHIR SMART User": "/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0", + "Grafana Admin": "/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41", + "Grafana Editor": "/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f", + "Grafana Viewer": "/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769", + "Graph Owner": "/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9", + "Guest Configuration Resource Contributor": "/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31", + "HDInsight Cluster Operator": "/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a", + "HDInsight Domain Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c", + "Hierarchy Settings Administrator": "/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d", + "Hybrid Server Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb", + "Hybrid Server Resource Administrator": "/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624", + "Impact Reader": "/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e", + "Impact Reporter": "/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a", + "Integration Service Environment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8", + "Integration Service Environment Developer": "/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec", + "Intelligent Systems Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e", + "IoT Hub Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f", + "IoT Hub Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3", + "IoT Hub Registry Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47", + "IoT Hub Twin Contributor": "/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c", + "Key Vault Administrator": "/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483", + "Key Vault Certificates Officer": "/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985", + "Key Vault Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395", + "Key Vault Crypto Officer": "/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603", + "Key Vault Crypto Service Encryption User": "/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6", + "Key Vault Crypto User": "/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424", + "Key Vault Reader": "/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2", + "Key Vault Secrets Officer": "/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7", + "Key Vault Secrets User": "/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6", + "Knowledge Consumer": "/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c", + "Kubernetes Agentless Operator": "/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6", + "Kubernetes Cluster - Azure Arc Onboarding": "/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41", + "Kubernetes Extension Contributor": "/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717", + "Kubernetes Namespace User": "/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a", + "Lab Assistant": "/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1", + "Lab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270", + "Lab Creator": "/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead", + "Lab Operator": "/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d", + "Lab Services Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f", + "Lab Services Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc", + "Load Test Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e", + "Load Test Owner": "/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6", + "Load Test Reader": "/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081", + "LocalNGFirewallAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2", + "LocalRulestacksAdministrator role": "/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20", + "Log Analytics Contributor": "/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293", + "Log Analytics Reader": "/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893", + "Logic App Contributor": "/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e", + "Logic App Operator": "/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe", + "Managed Application Contributor Role": "/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e", + "Managed Application Operator Role": "/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae", + "Managed Applications Reader": "/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44", + "Managed HSM contributor": "/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d", + "Managed Identity Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59", + "Managed Identity Operator": "/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830", + "Managed Services Registration assignment Delete Role": "/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46", + "Management Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c", + "Management Group Reader": "/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d", + "Media Services Account Administrator": "/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466", + "Media Services Live Events Administrator": "/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77", + "Media Services Media Operator": "/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c", + "Media Services Policy Administrator": "/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae", + "Media Services Streaming Endpoints Administrator": "/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804", + "Microsoft Sentinel Automation Contributor": "/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a", + "Microsoft Sentinel Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade", + "Microsoft Sentinel Playbook Operator": "/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5", + "Microsoft Sentinel Reader": "/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb", + "Microsoft Sentinel Responder": "/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056", + "Microsoft.Kubernetes connected cluster role": "/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f", + "Monitoring Contributor": "/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa", + "Monitoring Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136", + "Monitoring Metrics Publisher": "/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb", + "Monitoring Reader": "/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05", + "MySQL Backup And Export Operator": "/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0", + "Network Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7", + "New Relic APM Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237", + "Object Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b", + "Object Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9", + "Object Understanding Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745", + "Object Understanding Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6", + "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", + "PlayFab Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726", + "PlayFab Reader": "/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf", + "Policy Insights Data Writer (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84", + "Private DNS Zone Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f", + "Project Babylon Data Curator": "/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889", + "Project Babylon Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446", + "Project Babylon Data Source Administrator": "/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f", + "Purview role 1 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347", + "Purview role 2 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803", + "Purview role 3 (Deprecated)": "/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db", + "Quota Request Operator": "/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125", + "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", + "Reader and Data Access": "/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349", + "Redis Cache Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17", + "Remote Rendering Administrator": "/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e", + "Remote Rendering Client": "/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a", + "Reservation Purchaser": "/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689", + "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", + "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Scheduled Patching Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6", + "Scheduler Job Collections Contributor": "/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94", + "Schema Registry Contributor (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25", + "Schema Registry Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2", + "Search Index Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7", + "Search Index Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f", + "Search Service Contributor": "/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0", + "Security Admin": "/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd", + "Security Assessment Contributor": "/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5", + "Security Detonation Chamber Publisher": "/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500", + "Security Detonation Chamber Reader": "/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5", + "Security Detonation Chamber Submission Manager": "/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce", + "Security Detonation Chamber Submitter": "/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0", + "Security Manager (Legacy)": "/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10", + "Security Reader": "/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4", + "Services Hub Operator": "/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b", + "SignalR AccessKey Reader": "/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e", + "SignalR App Server": "/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7", + "SignalR REST API Owner": "/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521", + "SignalR REST API Reader": "/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035", + "SignalR Service Owner": "/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3", + "SignalR/Web PubSub Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761", + "Site Recovery Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567", + "Site Recovery Operator": "/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca", + "Site Recovery Reader": "/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149", + "Spatial Anchors Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827", + "Spatial Anchors Account Owner": "/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c", + "Spatial Anchors Account Reader": "/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413", + "SQL DB Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec", + "SQL Managed Instance Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d", + "SQL Security Manager": "/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3", + "SQL Server Contributor": "/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437", + "SqlDb Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57", + "SqlMI Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872", + "SqlVM Migration Role": "/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d", + "Storage Account Backup Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1", + "Storage Account Contributor": "/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab", + "Storage Account Key Operator Service Role": "/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12", + "Storage Blob Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe", + "Storage Blob Data Owner": "/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b", + "Storage Blob Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1", + "Storage Blob Delegator": "/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a", + "Storage File Data SMB Share Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb", + "Storage File Data SMB Share Elevated Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7", + "Storage File Data SMB Share Reader": "/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314", + "Storage Queue Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88", + "Storage Queue Data Message Processor": "/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed", + "Storage Queue Data Message Sender": "/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a", + "Storage Queue Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925", + "Storage Table Data Contributor": "/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3", + "Storage Table Data Reader": "/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6", + "Stream Analytics Query Tester": "/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf", + "Support Request Contributor": "/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e", + "Tag Contributor": "/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f", + "Template Spec Contributor": "/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b", + "Template Spec Reader": "/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e", + "Test Base Reader": "/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85", + "Traffic Manager Contributor": "/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7", + "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", + "Video Indexer Restricted Viewer": "/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb", + "Virtual Machine Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4", + "Virtual Machine Contributor": "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c", + "Virtual Machine Local User Login": "/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525", + "Virtual Machine User Login": "/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52", + "VM Scanner Operator": "/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd", + "Web Plan Contributor": "/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b", + "Web PubSub Service Owner (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4", + "Web PubSub Service Reader (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf", + "Website Contributor": "/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772", + "Windows Admin Center Administrator Login": "/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f", + "Workbook Contributor": "/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad", + "Workbook Reader": "/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d", + "WorkloadBuilder Migration Agent Role": "/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c" + }, + "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "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]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('ResourceGroupName')), 'Microsoft.Resources/deployments', format('c_AutomtnAcct-{0}', variables('AutomationAccountName')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "lnk_MonitoringResourcesDeployment", + "resourceGroup": "[if(variables('ResourceGroupCreate'), parameters('ResourceGroupName'), parameters('ResourceGroupName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "AllResourcesSameRG": { + "value": "[parameters('AllResourcesSameRG')]" + }, + "AVDResourceGroupId": { + "value": "[parameters('AVDResourceGroupId')]" + }, + "AutoResolveAlert": { + "value": "[parameters('AutoResolveAlert')]" + }, + "DistributionGroup": { + "value": "[parameters('DistributionGroup')]" + }, + "Environment": { + "value": "[parameters('Environment')]" + }, + "HostPoolInfo": { + "value": "[parameters('HostPoolInfo')]" + }, + "HostPools": { + "value": "[parameters('HostPools')]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "LogAnalyticsWorkspaceResourceId": { + "value": "[parameters('LogAnalyticsWorkspaceResourceId')]" + }, + "LogAlertsHostPool": { + "value": "[variables('LogAlertsHostPool')]" + }, + "LogAlertsStorage": { + "value": "[variables('LogAlertsStorage')]" + }, + "LogAlertsSvcHealth": { + "value": "[variables('LogAlertsSvcHealth')]" + }, + "MetricAlerts": { + "value": "[variables('MetricAlerts')]" + }, + "StorageAccountResourceIds": { + "value": "[parameters('StorageAccountResourceIds')]" + }, + "ActionGroupName": { + "value": "[variables('ActionGroupName')]" + }, + "ANFVolumeResourceIds": { + "value": "[parameters('ANFVolumeResourceIds')]" + }, + "Tags": { + "value": "[parameters('Tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "8842080406354769669" + } + }, + "parameters": { + "ActionGroupName": { + "type": "string" + }, + "AllResourcesSameRG": { + "type": "bool" + }, + "AutoResolveAlert": { + "type": "bool" + }, + "AVDResourceGroupId": { + "type": "string" + }, + "ANFVolumeResourceIds": { + "type": "array" + }, + "DistributionGroup": { + "type": "string" + }, + "Environment": { + "type": "string" + }, + "HostPoolInfo": { + "type": "array" + }, + "HostPools": { + "type": "array" + }, + "Location": { + "type": "string" + }, + "LogAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "LogAlertsStorage": { + "type": "array" + }, + "LogAlertsHostPool": { + "type": "array" + }, + "LogAlertsSvcHealth": { + "type": "array" + }, + "MetricAlerts": { + "type": "object" + }, + "StorageAccountResourceIds": { + "type": "array" + }, + "Tags": { + "type": "object" + } + }, + "variables": { + "SubscriptionId": "[subscription().subscriptionId]", + "SubscriptionName": "[replace(subscription().displayName, ' ', '')]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('ActionGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "emailReceivers": { + "value": [ + { + "emailAddress": "[parameters('DistributionGroup')]", + "name": "AVD Operations Admin(s)", + "useCommonAlertSchema": true + } + ] + }, + "enabled": { + "value": true + }, + "location": { + "value": "global" + }, + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[parameters('ActionGroupName')]" + }, + "groupShortName": { + "value": "AVDMetrics" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/actionGroups'), createObject('value', parameters('Tags')['Microsoft.Insights/actionGroups']), createObject('value', createObject()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "14548331241003575945" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the action group." + } + }, + "groupShortName": { + "type": "string", + "metadata": { + "description": "Required. The short name of the action group." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this action group is enabled. If an action group is not enabled, then none of its receivers will receive communications." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "emailReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of email receivers that are part of this action group." + } + }, + "smsReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of SMS receivers that are part of this action group." + } + }, + "webhookReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of webhook receivers that are part of this action group." + } + }, + "itsmReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of ITSM receivers that are part of this action group." + } + }, + "azureAppPushReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of AzureAppPush receivers that are part of this action group." + } + }, + "automationRunbookReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of AutomationRunbook receivers that are part of this action group." + } + }, + "voiceReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of voice receivers that are part of this action group." + } + }, + "logicAppReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of logic app receivers that are part of this action group." + } + }, + "azureFunctionReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of function receivers that are part of this action group." + } + }, + "armRoleReceivers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of ARM role receivers that are part of this action group. Roles are Azure RBAC roles and only built-in roles are supported." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "microsoft.insights/actionGroups", + "apiVersion": "2019-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "groupShortName": "[parameters('groupShortName')]", + "enabled": "[parameters('enabled')]", + "emailReceivers": "[if(empty(parameters('emailReceivers')), null(), parameters('emailReceivers'))]", + "smsReceivers": "[if(empty(parameters('smsReceivers')), null(), parameters('smsReceivers'))]", + "webhookReceivers": "[if(empty(parameters('webhookReceivers')), null(), parameters('webhookReceivers'))]", + "itsmReceivers": "[if(empty(parameters('itsmReceivers')), null(), parameters('itsmReceivers'))]", + "azureAppPushReceivers": "[if(empty(parameters('azureAppPushReceivers')), null(), parameters('azureAppPushReceivers'))]", + "automationRunbookReceivers": "[if(empty(parameters('automationRunbookReceivers')), null(), parameters('automationRunbookReceivers'))]", + "voiceReceivers": "[if(empty(parameters('voiceReceivers')), null(), parameters('voiceReceivers'))]", + "logicAppReceivers": "[if(empty(parameters('logicAppReceivers')), null(), parameters('logicAppReceivers'))]", + "azureFunctionReceivers": "[if(empty(parameters('azureFunctionReceivers')), null(), parameters('azureFunctionReceivers'))]", + "armRoleReceivers": "[if(empty(parameters('armRoleReceivers')), null(), parameters('armRoleReceivers'))]" + } + }, + { + "copy": { + "name": "actionGroup_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ActionGroup-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('microsoft.insights/actionGroups', 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.23.1.45101", + "templateHash": "7497695042150647825" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('microsoft.insights/actionGroups/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('microsoft.insights/actionGroups', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('microsoft.insights/actionGroups', parameters('name'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the action group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the action group ." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the action group ." + }, + "value": "[resourceId('microsoft.insights/actionGroups', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('microsoft.insights/actionGroups', parameters('name')), '2019-06-01', 'full').location]" + } + } + } + } + }, + { + "copy": { + "name": "metricAlertsVms", + "count": "[length(range(0, length(parameters('HostPoolInfo'))))]" + }, + "condition": "[not(parameters('AllResourcesSameRG'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[if(not(parameters('AllResourcesSameRG')), format('lnk_VMMtrcAlrts_m_{0}', split(parameters('HostPoolInfo')[range(0, length(parameters('HostPoolInfo')))[copyIndex()]].colHostPoolName, '/')[8]), 'lnk_VMMtrcAlrts_m_NA')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "HostPoolName": "[if(not(parameters('AllResourcesSameRG')), createObject('value', split(parameters('HostPoolInfo')[range(0, length(parameters('HostPoolInfo')))[copyIndex()]].colHostPoolName, '/')[8]), createObject('value', 'none'))]", + "Environment": { + "value": "[parameters('Environment')]" + }, + "VMResourceGroupId": { + "value": "[parameters('HostPoolInfo')[range(0, length(parameters('HostPoolInfo')))[copyIndex()]].colVMresGroup]" + }, + "MetricAlerts": { + "value": "[parameters('MetricAlerts')]" + }, + "Enabled": { + "value": false + }, + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "ActionGroupId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Tags": { + "value": "[parameters('Tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "18042637013033247" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "ActionGroupId": { + "type": "string" + }, + "Enabled": { + "type": "bool" + }, + "Environment": { + "type": "string" + }, + "HostPoolName": { + "type": "string" + }, + "MetricAlerts": { + "type": "object" + }, + "Tags": { + "type": "object" + }, + "Location": { + "type": "string" + }, + "VMResourceGroupId": { + "type": "string" + } + }, + "variables": { + "HostPoolResourceName": "[if(less(length(parameters('HostPoolName')), 20), parameters('HostPoolName'), skip(parameters('HostPoolName'), sub(length(parameters('HostPoolName')), 20)))]" + }, + "resources": [ + { + "copy": { + "name": "metricAlerts_VirtualMachines", + "count": "[length(range(0, length(parameters('MetricAlerts').virtualMachines)))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}', replace(parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].name, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}', replace(parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].displayName, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]" + }, + "criterias": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].criteria.allOf]" + }, + "location": { + "value": "global" + }, + "alertDescription": { + "value": "[replace(parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].description, 'xHostPoolNamex', parameters('HostPoolName'))]" + }, + "severity": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].severity]" + }, + "enabled": { + "value": "[parameters('Enabled')]" + }, + "scopes": { + "value": [ + "[parameters('VMResourceGroupId')]" + ] + }, + "evaluationFrequency": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].evaluationFrequency]" + }, + "windowSize": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].windowSize]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/metricAlerts'), createObject('value', parameters('Tags')['Microsoft.Insights/metricAlerts']), createObject('value', createObject()))]", + "targetResourceType": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].targetResourceType]" + }, + "targetResourceRegion": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + { + "actionGroupId": "[parameters('ActionGroupId')]", + "webHookProperties": {} + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "4325943649872987644" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the alert." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the alert." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this alert is enabled." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. The severity of the alert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "PT5M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H" + ], + "metadata": { + "description": "Optional. how often the metric alert is evaluated represented in ISO 8601 duration format." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "PT15M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H", + "PT6H", + "PT12H", + "P1D" + ], + "metadata": { + "description": "Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold." + } + }, + "scopes": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. the list of resource IDs that this metric alert is scoped to." + } + }, + "targetResourceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "targetResourceRegion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be auto resolved or not." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of actions to take when alert triggers." + } + }, + "alertCriteriaType": { + "type": "string", + "defaultValue": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "allowedValues": [ + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + ], + "metadata": { + "description": "Optional. Maps to the 'odata.type' field. Specifies the type of the alert criteria." + } + }, + "criterias": { + "type": "array", + "metadata": { + "description": "Required. Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "actionGroups", + "count": "[length(parameters('actions'))]", + "input": { + "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", + "webHookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties'), parameters('actions')[copyIndex('actionGroups')].webHookProperties, null())]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/metricAlerts", + "apiVersion": "2018-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('alertDescription')]", + "severity": "[parameters('severity')]", + "enabled": "[parameters('enabled')]", + "scopes": "[parameters('scopes')]", + "evaluationFrequency": "[parameters('evaluationFrequency')]", + "windowSize": "[parameters('windowSize')]", + "targetResourceType": "[parameters('targetResourceType')]", + "targetResourceRegion": "[parameters('targetResourceRegion')]", + "criteria": { + "odata.type": "[parameters('alertCriteriaType')]", + "allOf": "[parameters('criterias')]" + }, + "autoMitigate": "[parameters('autoMitigate')]", + "actions": "[variables('actionGroups')]" + } + }, + { + "copy": { + "name": "metricAlert_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-MetricAlert-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/metricAlerts', 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.23.1.45101", + "templateHash": "13865487144333348040" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/metricAlerts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/metricAlerts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the metric alert was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the metric alert." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the metric alert." + }, + "value": "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), '2018-03-01', 'full').location]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "metricAlertsVmsSingleRG", + "count": "[length(range(0, length(parameters('HostPools'))))]" + }, + "condition": "[parameters('AllResourcesSameRG')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('lnk_VMMtrcAlrts_s_{0}', split(parameters('HostPools')[range(0, length(parameters('HostPools')))[copyIndex()]], '/')[8])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "Environment": { + "value": "[parameters('Environment')]" + }, + "HostPoolName": { + "value": "[split(parameters('HostPools')[range(0, length(parameters('HostPools')))[copyIndex()]], '/')[8]]" + }, + "VMResourceGroupId": { + "value": "[parameters('AVDResourceGroupId')]" + }, + "MetricAlerts": { + "value": "[parameters('MetricAlerts')]" + }, + "Enabled": { + "value": false + }, + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "ActionGroupId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Tags": { + "value": "[parameters('Tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "18042637013033247" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "ActionGroupId": { + "type": "string" + }, + "Enabled": { + "type": "bool" + }, + "Environment": { + "type": "string" + }, + "HostPoolName": { + "type": "string" + }, + "MetricAlerts": { + "type": "object" + }, + "Tags": { + "type": "object" + }, + "Location": { + "type": "string" + }, + "VMResourceGroupId": { + "type": "string" + } + }, + "variables": { + "HostPoolResourceName": "[if(less(length(parameters('HostPoolName')), 20), parameters('HostPoolName'), skip(parameters('HostPoolName'), sub(length(parameters('HostPoolName')), 20)))]" + }, + "resources": [ + { + "copy": { + "name": "metricAlerts_VirtualMachines", + "count": "[length(range(0, length(parameters('MetricAlerts').virtualMachines)))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}', replace(parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].name, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}', replace(parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].displayName, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]" + }, + "criterias": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].criteria.allOf]" + }, + "location": { + "value": "global" + }, + "alertDescription": { + "value": "[replace(parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].description, 'xHostPoolNamex', parameters('HostPoolName'))]" + }, + "severity": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].severity]" + }, + "enabled": { + "value": "[parameters('Enabled')]" + }, + "scopes": { + "value": [ + "[parameters('VMResourceGroupId')]" + ] + }, + "evaluationFrequency": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].evaluationFrequency]" + }, + "windowSize": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].windowSize]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/metricAlerts'), createObject('value', parameters('Tags')['Microsoft.Insights/metricAlerts']), createObject('value', createObject()))]", + "targetResourceType": { + "value": "[parameters('MetricAlerts').virtualMachines[range(0, length(parameters('MetricAlerts').virtualMachines))[copyIndex()]].targetResourceType]" + }, + "targetResourceRegion": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + { + "actionGroupId": "[parameters('ActionGroupId')]", + "webHookProperties": {} + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "4325943649872987644" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the alert." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the alert." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this alert is enabled." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. The severity of the alert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "PT5M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H" + ], + "metadata": { + "description": "Optional. how often the metric alert is evaluated represented in ISO 8601 duration format." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "PT15M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H", + "PT6H", + "PT12H", + "P1D" + ], + "metadata": { + "description": "Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold." + } + }, + "scopes": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. the list of resource IDs that this metric alert is scoped to." + } + }, + "targetResourceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "targetResourceRegion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be auto resolved or not." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of actions to take when alert triggers." + } + }, + "alertCriteriaType": { + "type": "string", + "defaultValue": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "allowedValues": [ + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + ], + "metadata": { + "description": "Optional. Maps to the 'odata.type' field. Specifies the type of the alert criteria." + } + }, + "criterias": { + "type": "array", + "metadata": { + "description": "Required. Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "actionGroups", + "count": "[length(parameters('actions'))]", + "input": { + "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", + "webHookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties'), parameters('actions')[copyIndex('actionGroups')].webHookProperties, null())]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/metricAlerts", + "apiVersion": "2018-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('alertDescription')]", + "severity": "[parameters('severity')]", + "enabled": "[parameters('enabled')]", + "scopes": "[parameters('scopes')]", + "evaluationFrequency": "[parameters('evaluationFrequency')]", + "windowSize": "[parameters('windowSize')]", + "targetResourceType": "[parameters('targetResourceType')]", + "targetResourceRegion": "[parameters('targetResourceRegion')]", + "criteria": { + "odata.type": "[parameters('alertCriteriaType')]", + "allOf": "[parameters('criterias')]" + }, + "autoMitigate": "[parameters('autoMitigate')]", + "actions": "[variables('actionGroups')]" + } + }, + { + "copy": { + "name": "metricAlert_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-MetricAlert-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/metricAlerts', 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.23.1.45101", + "templateHash": "13865487144333348040" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/metricAlerts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/metricAlerts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the metric alert was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the metric alert." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the metric alert." + }, + "value": "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), '2018-03-01', 'full').location]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "storAccountMetric", + "count": "[length(range(0, length(parameters('StorageAccountResourceIds'))))]" + }, + "condition": "[greater(length(parameters('StorageAccountResourceIds')), 0)]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('lnk_StrAcctMtrcAlrts_{0}', split(parameters('StorageAccountResourceIds')[range(0, length(parameters('StorageAccountResourceIds')))[copyIndex()]], '/')[8])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "Enabled": { + "value": false + }, + "Environment": { + "value": "[parameters('Environment')]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "StorageAccountResourceID": { + "value": "[parameters('StorageAccountResourceIds')[range(0, length(parameters('StorageAccountResourceIds')))[copyIndex()]]]" + }, + "MetricAlertsStorageAcct": { + "value": "[parameters('MetricAlerts').storageAccounts]" + }, + "ActionGroupID": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Tags": { + "value": "[parameters('Tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "13090012199157395091" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "Enabled": { + "type": "bool" + }, + "Environment": { + "type": "string" + }, + "Location": { + "type": "string" + }, + "StorageAccountResourceID": { + "type": "string" + }, + "MetricAlertsStorageAcct": { + "type": "array" + }, + "ActionGroupID": { + "type": "string" + }, + "Tags": { + "type": "object" + } + }, + "resources": [ + { + "copy": { + "name": "metricAlerts_StorageAcct", + "count": "[length(range(0, length(parameters('MetricAlertsStorageAcct'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}-{2}', parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].name, split(parameters('StorageAccountResourceID'), '/')[8], parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}-{2}', parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].displayName, split(parameters('StorageAccountResourceID'), '/')[8], parameters('Environment'))]" + }, + "criterias": { + "value": "[parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].criteria.allOf]" + }, + "location": { + "value": "global" + }, + "alertDescription": { + "value": "[parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].description]" + }, + "severity": { + "value": "[parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].severity]" + }, + "enabled": { + "value": "[parameters('Enabled')]" + }, + "scopes": { + "value": [ + "[parameters('StorageAccountResourceID')]" + ] + }, + "evaluationFrequency": { + "value": "[parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].evaluationFrequency]" + }, + "windowSize": { + "value": "[parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].windowSize]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/metricAlerts'), createObject('value', parameters('Tags')['Microsoft.Insights/metricAlerts']), createObject('value', createObject()))]", + "targetResourceType": { + "value": "[parameters('MetricAlertsStorageAcct')[range(0, length(parameters('MetricAlertsStorageAcct')))[copyIndex()]].targetResourceType]" + }, + "targetResourceRegion": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + { + "actionGroupId": "[parameters('ActionGroupID')]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "4325943649872987644" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the alert." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the alert." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this alert is enabled." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. The severity of the alert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "PT5M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H" + ], + "metadata": { + "description": "Optional. how often the metric alert is evaluated represented in ISO 8601 duration format." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "PT15M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H", + "PT6H", + "PT12H", + "P1D" + ], + "metadata": { + "description": "Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold." + } + }, + "scopes": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. the list of resource IDs that this metric alert is scoped to." + } + }, + "targetResourceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "targetResourceRegion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be auto resolved or not." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of actions to take when alert triggers." + } + }, + "alertCriteriaType": { + "type": "string", + "defaultValue": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "allowedValues": [ + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + ], + "metadata": { + "description": "Optional. Maps to the 'odata.type' field. Specifies the type of the alert criteria." + } + }, + "criterias": { + "type": "array", + "metadata": { + "description": "Required. Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "actionGroups", + "count": "[length(parameters('actions'))]", + "input": { + "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", + "webHookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties'), parameters('actions')[copyIndex('actionGroups')].webHookProperties, null())]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/metricAlerts", + "apiVersion": "2018-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('alertDescription')]", + "severity": "[parameters('severity')]", + "enabled": "[parameters('enabled')]", + "scopes": "[parameters('scopes')]", + "evaluationFrequency": "[parameters('evaluationFrequency')]", + "windowSize": "[parameters('windowSize')]", + "targetResourceType": "[parameters('targetResourceType')]", + "targetResourceRegion": "[parameters('targetResourceRegion')]", + "criteria": { + "odata.type": "[parameters('alertCriteriaType')]", + "allOf": "[parameters('criterias')]" + }, + "autoMitigate": "[parameters('autoMitigate')]", + "actions": "[variables('actionGroups')]" + } + }, + { + "copy": { + "name": "metricAlert_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-MetricAlert-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/metricAlerts', 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.23.1.45101", + "templateHash": "13865487144333348040" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/metricAlerts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/metricAlerts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the metric alert was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the metric alert." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the metric alert." + }, + "value": "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), '2018-03-01', 'full').location]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "azureNetAppFilesMetric", + "count": "[length(range(0, length(parameters('ANFVolumeResourceIds'))))]" + }, + "condition": "[greater(length(parameters('ANFVolumeResourceIds')), 0)]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('lnk_ANFMtrcAlrts_{0}', split(parameters('ANFVolumeResourceIds')[range(0, length(parameters('ANFVolumeResourceIds')))[copyIndex()]], '/')[12])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "Enabled": { + "value": false + }, + "Environment": { + "value": "[parameters('Environment')]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "ANFVolumeResourceID": { + "value": "[parameters('ANFVolumeResourceIds')[range(0, length(parameters('ANFVolumeResourceIds')))[copyIndex()]]]" + }, + "MetricAlertsANF": { + "value": "[parameters('MetricAlerts').anf]" + }, + "ActionGroupID": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Tags": { + "value": "[parameters('Tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "12335470402732189346" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "Enabled": { + "type": "bool" + }, + "Environment": { + "type": "string" + }, + "Location": { + "type": "string" + }, + "MetricAlertsANF": { + "type": "array" + }, + "ANFVolumeResourceID": { + "type": "string" + }, + "ActionGroupID": { + "type": "string" + }, + "Tags": { + "type": "object" + } + }, + "resources": [ + { + "copy": { + "name": "metricAlerts_VirtualMachines", + "count": "[length(range(0, length(parameters('MetricAlertsANF'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}-{2}', parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].name, split(parameters('ANFVolumeResourceID'), '/')[12], parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}-{2}', parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].displayName, split(parameters('ANFVolumeResourceID'), '/')[12], parameters('Environment'))]" + }, + "criterias": { + "value": "[parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].criteria.allOf]" + }, + "location": { + "value": "global" + }, + "alertDescription": { + "value": "[parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].description]" + }, + "severity": { + "value": "[parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].severity]" + }, + "enabled": { + "value": "[parameters('Enabled')]" + }, + "scopes": { + "value": [ + "[parameters('ANFVolumeResourceID')]" + ] + }, + "evaluationFrequency": { + "value": "[parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].evaluationFrequency]" + }, + "windowSize": { + "value": "[parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].windowSize]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/metricAlerts'), createObject('value', parameters('Tags')['Microsoft.Insights/metricAlerts']), createObject('value', createObject()))]", + "targetResourceType": { + "value": "[parameters('MetricAlertsANF')[range(0, length(parameters('MetricAlertsANF')))[copyIndex()]].targetResourceType]" + }, + "targetResourceRegion": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + { + "actionGroupId": "[parameters('ActionGroupID')]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "4325943649872987644" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the alert." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the alert." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this alert is enabled." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. The severity of the alert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "PT5M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H" + ], + "metadata": { + "description": "Optional. how often the metric alert is evaluated represented in ISO 8601 duration format." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "PT15M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H", + "PT6H", + "PT12H", + "P1D" + ], + "metadata": { + "description": "Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold." + } + }, + "scopes": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. the list of resource IDs that this metric alert is scoped to." + } + }, + "targetResourceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "targetResourceRegion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be auto resolved or not." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of actions to take when alert triggers." + } + }, + "alertCriteriaType": { + "type": "string", + "defaultValue": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "allowedValues": [ + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + ], + "metadata": { + "description": "Optional. Maps to the 'odata.type' field. Specifies the type of the alert criteria." + } + }, + "criterias": { + "type": "array", + "metadata": { + "description": "Required. Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "actionGroups", + "count": "[length(parameters('actions'))]", + "input": { + "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", + "webHookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties'), parameters('actions')[copyIndex('actionGroups')].webHookProperties, null())]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/metricAlerts", + "apiVersion": "2018-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('alertDescription')]", + "severity": "[parameters('severity')]", + "enabled": "[parameters('enabled')]", + "scopes": "[parameters('scopes')]", + "evaluationFrequency": "[parameters('evaluationFrequency')]", + "windowSize": "[parameters('windowSize')]", + "targetResourceType": "[parameters('targetResourceType')]", + "targetResourceRegion": "[parameters('targetResourceRegion')]", + "criteria": { + "odata.type": "[parameters('alertCriteriaType')]", + "allOf": "[parameters('criterias')]" + }, + "autoMitigate": "[parameters('autoMitigate')]", + "actions": "[variables('actionGroups')]" + } + }, + { + "copy": { + "name": "metricAlert_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-MetricAlert-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/metricAlerts', 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.23.1.45101", + "templateHash": "13865487144333348040" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/metricAlerts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/metricAlerts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the metric alert was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the metric alert." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the metric alert." + }, + "value": "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), '2018-03-01', 'full').location]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "fileServicesMetric", + "count": "[length(range(0, length(parameters('StorageAccountResourceIds'))))]" + }, + "condition": "[greater(length(parameters('StorageAccountResourceIds')), 0)]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('lnk_FlSvcsMtrcAlrts_{0}', range(0, length(parameters('StorageAccountResourceIds')))[copyIndex()])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "Enabled": { + "value": false + }, + "Environment": { + "value": "[parameters('Environment')]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "StorageAccountResourceID": { + "value": "[parameters('StorageAccountResourceIds')[range(0, length(parameters('StorageAccountResourceIds')))[copyIndex()]]]" + }, + "MetricAlertsFileShares": { + "value": "[parameters('MetricAlerts').fileShares]" + }, + "ActionGroupID": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Tags": { + "value": "[parameters('Tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "12574331042221135071" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "Enabled": { + "type": "bool" + }, + "Environment": { + "type": "string" + }, + "Location": { + "type": "string" + }, + "StorageAccountResourceID": { + "type": "string" + }, + "MetricAlertsFileShares": { + "type": "array" + }, + "ActionGroupID": { + "type": "string" + }, + "Tags": { + "type": "object" + } + }, + "variables": { + "FileServicesResourceID": "[format('{0}/fileServices/default', parameters('StorageAccountResourceID'))]" + }, + "resources": [ + { + "copy": { + "name": "metricAlerts_FileServices", + "count": "[length(range(0, length(parameters('MetricAlertsFileShares'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}-{2}', parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].name, split(variables('FileServicesResourceID'), '/')[8], parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}-{2}', parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].displayName, split(variables('FileServicesResourceID'), '/')[8], parameters('Environment'))]" + }, + "criterias": { + "value": "[parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].criteria.allOf]" + }, + "location": { + "value": "global" + }, + "alertDescription": { + "value": "[parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].description]" + }, + "severity": { + "value": "[parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].severity]" + }, + "enabled": { + "value": "[parameters('Enabled')]" + }, + "scopes": { + "value": [ + "[variables('FileServicesResourceID')]" + ] + }, + "evaluationFrequency": { + "value": "[parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].evaluationFrequency]" + }, + "windowSize": { + "value": "[parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].windowSize]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/metricAlerts'), createObject('value', parameters('Tags')['Microsoft.Insights/metricAlerts']), createObject('value', createObject()))]", + "targetResourceType": { + "value": "[parameters('MetricAlertsFileShares')[range(0, length(parameters('MetricAlertsFileShares')))[copyIndex()]].targetResourceType]" + }, + "targetResourceRegion": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + { + "actionGroupId": "[parameters('ActionGroupID')]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "4325943649872987644" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the alert." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the alert." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this alert is enabled." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. The severity of the alert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "PT5M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H" + ], + "metadata": { + "description": "Optional. how often the metric alert is evaluated represented in ISO 8601 duration format." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "PT15M", + "allowedValues": [ + "PT1M", + "PT5M", + "PT15M", + "PT30M", + "PT1H", + "PT6H", + "PT12H", + "P1D" + ], + "metadata": { + "description": "Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold." + } + }, + "scopes": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. the list of resource IDs that this metric alert is scoped to." + } + }, + "targetResourceType": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "targetResourceRegion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be auto resolved or not." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of actions to take when alert triggers." + } + }, + "alertCriteriaType": { + "type": "string", + "defaultValue": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "allowedValues": [ + "Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria", + "Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria" + ], + "metadata": { + "description": "Optional. Maps to the 'odata.type' field. Specifies the type of the alert criteria." + } + }, + "criterias": { + "type": "array", + "metadata": { + "description": "Required. Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "actionGroups", + "count": "[length(parameters('actions'))]", + "input": { + "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", + "webHookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webHookProperties'), parameters('actions')[copyIndex('actionGroups')].webHookProperties, null())]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/metricAlerts", + "apiVersion": "2018-03-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('alertDescription')]", + "severity": "[parameters('severity')]", + "enabled": "[parameters('enabled')]", + "scopes": "[parameters('scopes')]", + "evaluationFrequency": "[parameters('evaluationFrequency')]", + "windowSize": "[parameters('windowSize')]", + "targetResourceType": "[parameters('targetResourceType')]", + "targetResourceRegion": "[parameters('targetResourceRegion')]", + "criteria": { + "odata.type": "[parameters('alertCriteriaType')]", + "allOf": "[parameters('criterias')]" + }, + "autoMitigate": "[parameters('autoMitigate')]", + "actions": "[variables('actionGroups')]" + } + }, + { + "copy": { + "name": "metricAlert_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-MetricAlert-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/metricAlerts', 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.23.1.45101", + "templateHash": "13865487144333348040" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/metricAlerts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/metricAlerts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + ] + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the metric alert was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the metric alert." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the metric alert." + }, + "value": "[resourceId('Microsoft.Insights/metricAlerts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/metricAlerts', parameters('name')), '2018-03-01', 'full').location]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "logAlertStorage", + "count": "[length(range(0, length(parameters('LogAlertsStorage'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}', parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].name]" + }, + "autoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "criterias": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].criteria]" + }, + "scopes": { + "value": [ + "[parameters('LogAnalyticsWorkspaceResourceId')]" + ] + }, + "location": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + ] + }, + "alertDisplayName": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].displayName]" + }, + "alertDescription": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].description]" + }, + "enabled": { + "value": false + }, + "evaluationFrequency": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].evaluationFrequency]" + }, + "severity": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].severity]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/scheduledQueryRules'), createObject('value', parameters('Tags')['Microsoft.Insights/scheduledQueryRules']), createObject('value', createObject()))]", + "windowSize": { + "value": "[parameters('LogAlertsStorage')[range(0, length(parameters('LogAlertsStorage')))[copyIndex()]].windowSize]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "87049037068146934" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Alert." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "alertDisplayName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The display name of the scheduled query rule." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the scheduled query rule." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag which indicates whether this scheduled query rule is enabled." + } + }, + "kind": { + "type": "string", + "defaultValue": "LogAlert", + "allowedValues": [ + "LogAlert", + "LogToMetric" + ], + "metadata": { + "description": "Optional. Indicates the type of scheduled query rule." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert." + } + }, + "queryTimeRange": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert." + } + }, + "skipQueryValidation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert." + } + }, + "targetResourceTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "scopes": { + "type": "array", + "metadata": { + "description": "Required. The list of resource IDs that this scheduled query rule is scoped to." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Actions to invoke when the alert fires." + } + }, + "criterias": { + "type": "object", + "metadata": { + "description": "Required. The rule criteria that defines the conditions of the scheduled query rule." + } + }, + "suppressForMinutes": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/scheduledQueryRules", + "apiVersion": "2021-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "actions": { + "actionGroups": "[parameters('actions')]", + "customProperties": {} + }, + "autoMitigate": "[if(equals(parameters('kind'), 'LogAlert'), parameters('autoMitigate'), null())]", + "criteria": "[parameters('criterias')]", + "description": "[parameters('alertDescription')]", + "displayName": "[if(not(empty(parameters('alertDisplayName'))), parameters('alertDisplayName'), parameters('name'))]", + "enabled": "[parameters('enabled')]", + "evaluationFrequency": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('evaluationFrequency')))), parameters('evaluationFrequency'), null())]", + "muteActionsDuration": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('suppressForMinutes')))), parameters('suppressForMinutes'), null())]", + "overrideQueryTimeRange": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('queryTimeRange')))), parameters('queryTimeRange'), null())]", + "scopes": "[parameters('scopes')]", + "severity": "[if(equals(parameters('kind'), 'LogAlert'), parameters('severity'), null())]", + "skipQueryValidation": "[if(equals(parameters('kind'), 'LogAlert'), parameters('skipQueryValidation'), null())]", + "targetResourceTypes": "[if(equals(parameters('kind'), 'LogAlert'), parameters('targetResourceTypes'), null())]", + "windowSize": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('windowSize')))), parameters('windowSize'), null())]" + } + }, + { + "copy": { + "name": "queryRule_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-QueryRule-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/scheduledQueryRules', 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.23.1.45101", + "templateHash": "697362920563967240" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/scheduledQueryRules/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/scheduledQueryRules', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the created query rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the created query rule." + }, + "value": "[resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The Resource Group of the created query rule." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name')), '2021-02-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "logAlertHostPoolQueriesMapped", + "count": "[length(parameters('HostPoolInfo'))]" + }, + "condition": "[not(parameters('AllResourcesSameRG'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('lnk_HPAlrts-{0}', split(parameters('HostPoolInfo')[copyIndex()].colHostPoolName, '/')[8])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "ActionGroupId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Environment": { + "value": "[parameters('Environment')]" + }, + "HostPoolName": { + "value": "[split(parameters('HostPoolInfo')[copyIndex()].colHostPoolName, '/')[8]]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "LogAlertsHostPool": { + "value": "[parameters('LogAlertsHostPool')]" + }, + "LogAnalyticsWorkspaceResourceId": { + "value": "[parameters('LogAnalyticsWorkspaceResourceId')]" + }, + "Tags": { + "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.23.1.45101", + "templateHash": "6763740487781585899" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "ActionGroupId": { + "type": "string" + }, + "Environment": { + "type": "string" + }, + "HostPoolName": { + "type": "string" + }, + "LogAlertsHostPool": { + "type": "array" + }, + "LogAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "Location": { + "type": "string" + }, + "Tags": { + "type": "object" + } + }, + "variables": { + "HostPoolResourceName": "[if(less(length(parameters('HostPoolName')), 20), parameters('HostPoolName'), skip(parameters('HostPoolName'), sub(length(parameters('HostPoolName')), 20)))]" + }, + "resources": [ + { + "copy": { + "name": "logAlertHostPoolQueries", + "count": "[length(range(0, length(parameters('LogAlertsHostPool'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].name, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].name, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "criterias": { + "value": { + "allOf": [ + { + "query": "[replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].query, 'xHostPoolNamex', parameters('HostPoolName'))]", + "timeAggregation": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].timeAggregation]", + "dimensions": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].dimensions]", + "operator": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].operator]", + "threshold": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].threshold]", + "failingPeriods": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].failingPeriods]" + } + ] + } + }, + "scopes": { + "value": [ + "[parameters('LogAnalyticsWorkspaceResourceId')]" + ] + }, + "location": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + "[parameters('ActionGroupId')]" + ] + }, + "alertDisplayName": { + "value": "[format('{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].displayName, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]" + }, + "alertDescription": { + "value": "[format('{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].description, 'xHostPoolNamex', parameters('HostPoolName')), parameters('Environment'))]" + }, + "enabled": { + "value": false + }, + "evaluationFrequency": { + "value": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].evaluationFrequency]" + }, + "severity": { + "value": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].severity]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/scheduledQueryRules'), createObject('value', parameters('Tags')['Microsoft.Insights/scheduledQueryRules']), createObject('value', createObject()))]", + "windowSize": { + "value": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].windowSize]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "87049037068146934" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Alert." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "alertDisplayName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The display name of the scheduled query rule." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the scheduled query rule." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag which indicates whether this scheduled query rule is enabled." + } + }, + "kind": { + "type": "string", + "defaultValue": "LogAlert", + "allowedValues": [ + "LogAlert", + "LogToMetric" + ], + "metadata": { + "description": "Optional. Indicates the type of scheduled query rule." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert." + } + }, + "queryTimeRange": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert." + } + }, + "skipQueryValidation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert." + } + }, + "targetResourceTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "scopes": { + "type": "array", + "metadata": { + "description": "Required. The list of resource IDs that this scheduled query rule is scoped to." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Actions to invoke when the alert fires." + } + }, + "criterias": { + "type": "object", + "metadata": { + "description": "Required. The rule criteria that defines the conditions of the scheduled query rule." + } + }, + "suppressForMinutes": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/scheduledQueryRules", + "apiVersion": "2021-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "actions": { + "actionGroups": "[parameters('actions')]", + "customProperties": {} + }, + "autoMitigate": "[if(equals(parameters('kind'), 'LogAlert'), parameters('autoMitigate'), null())]", + "criteria": "[parameters('criterias')]", + "description": "[parameters('alertDescription')]", + "displayName": "[if(not(empty(parameters('alertDisplayName'))), parameters('alertDisplayName'), parameters('name'))]", + "enabled": "[parameters('enabled')]", + "evaluationFrequency": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('evaluationFrequency')))), parameters('evaluationFrequency'), null())]", + "muteActionsDuration": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('suppressForMinutes')))), parameters('suppressForMinutes'), null())]", + "overrideQueryTimeRange": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('queryTimeRange')))), parameters('queryTimeRange'), null())]", + "scopes": "[parameters('scopes')]", + "severity": "[if(equals(parameters('kind'), 'LogAlert'), parameters('severity'), null())]", + "skipQueryValidation": "[if(equals(parameters('kind'), 'LogAlert'), parameters('skipQueryValidation'), null())]", + "targetResourceTypes": "[if(equals(parameters('kind'), 'LogAlert'), parameters('targetResourceTypes'), null())]", + "windowSize": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('windowSize')))), parameters('windowSize'), null())]" + } + }, + { + "copy": { + "name": "queryRule_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-QueryRule-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/scheduledQueryRules', 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.23.1.45101", + "templateHash": "697362920563967240" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/scheduledQueryRules/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/scheduledQueryRules', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the created query rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the created query rule." + }, + "value": "[resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The Resource Group of the created query rule." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name')), '2021-02-01-preview', 'full').location]" + } + } + } + } + } + ], + "outputs": { + "HostPoolResourceName": { + "type": "string", + "value": "[variables('HostPoolResourceName')]" + }, + "HostPoolName": { + "type": "string", + "value": "[parameters('HostPoolName')]" + }, + "HostPoolNameLength": { + "type": "int", + "value": "[length(parameters('HostPoolName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "logAlertHostPoolQueriesSingleRG", + "count": "[length(parameters('HostPools'))]" + }, + "condition": "[parameters('AllResourcesSameRG')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('lnk_HPAlrts-{0}', split(parameters('HostPools')[copyIndex()], '/')[8])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "AutoMitigate": { + "value": "[parameters('AutoResolveAlert')]" + }, + "ActionGroupId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + }, + "Environment": { + "value": "[parameters('Environment')]" + }, + "HostPoolName": { + "value": "[split(parameters('HostPools')[copyIndex()], '/')[8]]" + }, + "Location": { + "value": "[parameters('Location')]" + }, + "LogAlertsHostPool": { + "value": "[parameters('LogAlertsHostPool')]" + }, + "LogAnalyticsWorkspaceResourceId": { + "value": "[parameters('LogAnalyticsWorkspaceResourceId')]" + }, + "Tags": { + "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.23.1.45101", + "templateHash": "6763740487781585899" + } + }, + "parameters": { + "AutoMitigate": { + "type": "bool" + }, + "ActionGroupId": { + "type": "string" + }, + "Environment": { + "type": "string" + }, + "HostPoolName": { + "type": "string" + }, + "LogAlertsHostPool": { + "type": "array" + }, + "LogAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "Location": { + "type": "string" + }, + "Tags": { + "type": "object" + } + }, + "variables": { + "HostPoolResourceName": "[if(less(length(parameters('HostPoolName')), 20), parameters('HostPoolName'), skip(parameters('HostPoolName'), sub(length(parameters('HostPoolName')), 20)))]" + }, + "resources": [ + { + "copy": { + "name": "logAlertHostPoolQueries", + "count": "[length(range(0, length(parameters('LogAlertsHostPool'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].name, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].name, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]" + }, + "autoMitigate": { + "value": "[parameters('AutoMitigate')]" + }, + "criterias": { + "value": { + "allOf": [ + { + "query": "[replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].query, 'xHostPoolNamex', parameters('HostPoolName'))]", + "timeAggregation": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].timeAggregation]", + "dimensions": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].dimensions]", + "operator": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].operator]", + "threshold": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].threshold]", + "failingPeriods": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].criteria.allOf[0].failingPeriods]" + } + ] + } + }, + "scopes": { + "value": [ + "[parameters('LogAnalyticsWorkspaceResourceId')]" + ] + }, + "location": { + "value": "[parameters('Location')]" + }, + "actions": { + "value": [ + "[parameters('ActionGroupId')]" + ] + }, + "alertDisplayName": { + "value": "[format('{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].displayName, 'xHostPoolNamex', variables('HostPoolResourceName')), parameters('Environment'))]" + }, + "alertDescription": { + "value": "[format('{0}-{1}', replace(parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].description, 'xHostPoolNamex', parameters('HostPoolName')), parameters('Environment'))]" + }, + "enabled": { + "value": false + }, + "evaluationFrequency": { + "value": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].evaluationFrequency]" + }, + "severity": { + "value": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].severity]" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/scheduledQueryRules'), createObject('value', parameters('Tags')['Microsoft.Insights/scheduledQueryRules']), createObject('value', createObject()))]", + "windowSize": { + "value": "[parameters('LogAlertsHostPool')[range(0, length(parameters('LogAlertsHostPool')))[copyIndex()]].windowSize]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "87049037068146934" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Alert." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "alertDisplayName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The display name of the scheduled query rule." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the scheduled query rule." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag which indicates whether this scheduled query rule is enabled." + } + }, + "kind": { + "type": "string", + "defaultValue": "LogAlert", + "allowedValues": [ + "LogAlert", + "LogToMetric" + ], + "metadata": { + "description": "Optional. Indicates the type of scheduled query rule." + } + }, + "autoMitigate": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert." + } + }, + "queryTimeRange": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert." + } + }, + "skipQueryValidation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert." + } + }, + "targetResourceTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "scopes": { + "type": "array", + "metadata": { + "description": "Required. The list of resource IDs that this scheduled query rule is scoped to." + } + }, + "severity": { + "type": "int", + "defaultValue": 3, + "allowedValues": [ + 0, + 1, + 2, + 3, + 4 + ], + "metadata": { + "description": "Optional. Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert." + } + }, + "evaluationFrequency": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert." + } + }, + "windowSize": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Actions to invoke when the alert fires." + } + }, + "criterias": { + "type": "object", + "metadata": { + "description": "Required. The rule criteria that defines the conditions of the scheduled query rule." + } + }, + "suppressForMinutes": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/scheduledQueryRules", + "apiVersion": "2021-02-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "actions": { + "actionGroups": "[parameters('actions')]", + "customProperties": {} + }, + "autoMitigate": "[if(equals(parameters('kind'), 'LogAlert'), parameters('autoMitigate'), null())]", + "criteria": "[parameters('criterias')]", + "description": "[parameters('alertDescription')]", + "displayName": "[if(not(empty(parameters('alertDisplayName'))), parameters('alertDisplayName'), parameters('name'))]", + "enabled": "[parameters('enabled')]", + "evaluationFrequency": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('evaluationFrequency')))), parameters('evaluationFrequency'), null())]", + "muteActionsDuration": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('suppressForMinutes')))), parameters('suppressForMinutes'), null())]", + "overrideQueryTimeRange": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('queryTimeRange')))), parameters('queryTimeRange'), null())]", + "scopes": "[parameters('scopes')]", + "severity": "[if(equals(parameters('kind'), 'LogAlert'), parameters('severity'), null())]", + "skipQueryValidation": "[if(equals(parameters('kind'), 'LogAlert'), parameters('skipQueryValidation'), null())]", + "targetResourceTypes": "[if(equals(parameters('kind'), 'LogAlert'), parameters('targetResourceTypes'), null())]", + "windowSize": "[if(and(equals(parameters('kind'), 'LogAlert'), not(empty(parameters('windowSize')))), parameters('windowSize'), null())]" + } + }, + { + "copy": { + "name": "queryRule_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-QueryRule-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/scheduledQueryRules', 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.23.1.45101", + "templateHash": "697362920563967240" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/scheduledQueryRules/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/scheduledQueryRules', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the created query rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the created query rule." + }, + "value": "[resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The Resource Group of the created query rule." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/scheduledQueryRules', parameters('name')), '2021-02-01-preview', 'full').location]" + } + } + } + } + } + ], + "outputs": { + "HostPoolResourceName": { + "type": "string", + "value": "[variables('HostPoolResourceName')]" + }, + "HostPoolName": { + "type": "string", + "value": "[parameters('HostPoolName')]" + }, + "HostPoolNameLength": { + "type": "int", + "value": "[length(parameters('HostPoolName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + }, + { + "copy": { + "name": "logAlertSvcHealth", + "count": "[length(range(0, length(parameters('LogAlertsSvcHealth'))))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('c_{0}', parameters('LogAlertsSvcHealth')[range(0, length(parameters('LogAlertsSvcHealth')))[copyIndex()]].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableDefaultTelemetry": { + "value": false + }, + "name": { + "value": "[format('{0}-{1}', parameters('LogAlertsSvcHealth')[range(0, length(parameters('LogAlertsSvcHealth')))[copyIndex()]].displayName, variables('SubscriptionName'))]" + }, + "enabled": { + "value": false + }, + "location": { + "value": "global" + }, + "tags": "[if(contains(parameters('Tags'), 'Microsoft.Insights/activityLogAlerts'), createObject('value', parameters('Tags')['Microsoft.Insights/activityLogAlerts']), createObject('value', createObject()))]", + "scopes": { + "value": [ + "[format('/subscriptions/{0}', variables('SubscriptionId'))]" + ] + }, + "conditions": { + "value": [ + { + "field": "category", + "equals": "ServiceHealth" + }, + { + "anyOf": "[parameters('LogAlertsSvcHealth')[range(0, length(parameters('LogAlertsSvcHealth')))[copyIndex()]].anyof]" + }, + { + "field": "properties.impactedServices[*].ServiceName", + "containsAny": [ + "Windows Virtual Desktop" + ] + }, + { + "field": "properties.impactedServices[*].ImpactedRegions[*].RegionName", + "containsAny": [ + "[parameters('Location')]" + ] + } + ] + }, + "actions": { + "value": [ + "[reference(resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName')), '2022-09-01').outputs.resourceId.value]" + ] + }, + "alertDescription": { + "value": "[parameters('LogAlertsSvcHealth')[range(0, length(parameters('LogAlertsSvcHealth')))[copyIndex()]].description]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "13713639001813517941" + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the alert." + } + }, + "alertDescription": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Description of the alert." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether this alert is enabled." + } + }, + "scopes": { + "type": "array", + "defaultValue": [ + "[subscription().id]" + ], + "metadata": { + "description": "Optional. The list of resource IDs that this Activity Log Alert is scoped to." + } + }, + "actions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The list of actions to take when alert triggers." + } + }, + "conditions": { + "type": "array", + "metadata": { + "description": "Required. The condition that will cause this alert to activate. Array of objects." + } + }, + "roleAssignments": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable telemetry via a Globally Unique Identifier (GUID)." + } + } + }, + "variables": { + "copy": [ + { + "name": "actionGroups", + "count": "[length(parameters('actions'))]", + "input": { + "actionGroupId": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'actionGroupId'), parameters('actions')[copyIndex('actionGroups')].actionGroupId, parameters('actions')[copyIndex('actionGroups')])]", + "webhookProperties": "[if(contains(parameters('actions')[copyIndex('actionGroups')], 'webhookProperties'), parameters('actions')[copyIndex('actionGroups')].webhookProperties, null())]" + } + } + ] + }, + "resources": [ + { + "condition": "[parameters('enableDefaultTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-{0}', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [] + } + } + }, + { + "type": "Microsoft.Insights/activityLogAlerts", + "apiVersion": "2020-10-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "scopes": "[parameters('scopes')]", + "condition": { + "allOf": "[parameters('conditions')]" + }, + "actions": { + "actionGroups": "[variables('actionGroups')]" + }, + "enabled": "[parameters('enabled')]", + "description": "[parameters('alertDescription')]" + } + }, + { + "copy": { + "name": "activityLogAlert_roleAssignments", + "count": "[length(parameters('roleAssignments'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ActivityLogAlert-Rbac-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "description": "[if(contains(parameters('roleAssignments')[copyIndex()], 'description'), createObject('value', parameters('roleAssignments')[copyIndex()].description), createObject('value', ''))]", + "principalIds": { + "value": "[parameters('roleAssignments')[copyIndex()].principalIds]" + }, + "principalType": "[if(contains(parameters('roleAssignments')[copyIndex()], 'principalType'), createObject('value', parameters('roleAssignments')[copyIndex()].principalType), createObject('value', ''))]", + "roleDefinitionIdOrName": { + "value": "[parameters('roleAssignments')[copyIndex()].roleDefinitionIdOrName]" + }, + "condition": "[if(contains(parameters('roleAssignments')[copyIndex()], 'condition'), createObject('value', parameters('roleAssignments')[copyIndex()].condition), createObject('value', ''))]", + "delegatedManagedIdentityResourceId": "[if(contains(parameters('roleAssignments')[copyIndex()], 'delegatedManagedIdentityResourceId'), createObject('value', parameters('roleAssignments')[copyIndex()].delegatedManagedIdentityResourceId), createObject('value', ''))]", + "resourceId": { + "value": "[resourceId('Microsoft.Insights/activityLogAlerts', 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.23.1.45101", + "templateHash": "8205837903548946509" + } + }, + "parameters": { + "principalIds": { + "type": "array", + "metadata": { + "description": "Required. The IDs of the principals to assign the role to." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource to apply the role assignment to." + } + }, + "principalType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "ServicePrincipal", + "Group", + "User", + "ForeignGroup", + "Device", + "" + ], + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "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", + "defaultValue": "2.0", + "allowedValues": [ + "2.0" + ], + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Id of the delegated managed identity resource." + } + } + }, + "variables": { + "builtInRoleNames": { + "API Management Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c')]", + "API Management Service Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61')]", + "API Management Service Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d')]", + "Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Automation Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867')]", + "Automation Job Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f')]", + "Automation Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404')]", + "Automation Runbook Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5')]", + "Avere Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a')]", + "Azure Arc Enabled Kubernetes Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd')]", + "Azure Arc Kubernetes Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96')]", + "Azure Arc Kubernetes Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2')]", + "Azure Arc Kubernetes Viewer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4')]", + "Azure Arc Kubernetes Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1')]", + "Azure Arc ScVmm Administrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87')]", + "Azure Arc ScVmm Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda')]", + "Azure Arc ScVmm Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9')]", + "Azure Arc ScVmm VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b')]", + "Azure Arc VMware Administrator role ": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f')]", + "Azure Arc VMware Private Cloud User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83')]", + "Azure Arc VMware Private Clouds Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa')]", + "Azure Arc VMware VM Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb')]", + "Azure Center for SAP solutions administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7')]", + "Azure Center for SAP solutions reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b')]", + "BizTalk Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342')]", + "CDN Endpoint Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45')]", + "CDN Endpoint Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd')]", + "CDN Profile Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432')]", + "CDN Profile Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af')]", + "Classic Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f')]", + "Classic Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25')]", + "Classic Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]", + "ClearDB MySQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe')]", + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Collaborative Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352')]", + "Collaborative Runtime Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102')]", + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "Data Factory Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5')]", + "Data Lake Analytics Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88')]", + "Data Purger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90')]", + "Desktop Virtualization Application Group Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "Desktop Virtualization Application Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55')]", + "Desktop Virtualization Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "Desktop Virtualization Host Pool Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc')]", + "Desktop Virtualization Host Pool Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868')]", + "Desktop Virtualization Session Host Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "Desktop Virtualization User Session Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "Desktop Virtualization Workspace Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b')]", + "Desktop Virtualization Workspace Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d')]", + "Device Update Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a')]", + "Device Update Content Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98')]", + "Device Update Content Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b')]", + "Device Update Deployments Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432')]", + "Device Update Deployments Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f')]", + "Device Update Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "EventGrid Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de')]", + "EventGrid EventSubscription Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443')]", + "HDInsight Cluster Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a')]", + "Intelligent Systems Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e')]", + "Key Vault Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')]", + "Key Vault Certificates Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985')]", + "Key Vault Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395')]", + "Key Vault Crypto Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603')]", + "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", + "Key Vault Secrets Officer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7')]", + "Kubernetes Cluster - Azure Arc Onboarding": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41')]", + "Kubernetes Extension Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717')]", + "Lab Assistant": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1')]", + "Lab Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270')]", + "Lab Creator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead')]", + "Lab Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d')]", + "Lab Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f')]", + "Load Test Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e')]", + "Load Test Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6')]", + "Load Test Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081')]", + "LocalNGFirewallAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2')]", + "LocalRulestacksAdministrator role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Logic App Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e')]", + "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", + "Managed Application Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e')]", + "Managed Application Operator Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae')]", + "Managed Applications Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Media Services Account Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466')]", + "Media Services Live Events Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77')]", + "Media Services Media Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c')]", + "Media Services Policy Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae')]", + "Media Services Streaming Endpoints Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804')]", + "Microsoft Sentinel Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade')]", + "Microsoft Sentinel Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb')]", + "Microsoft Sentinel Responder": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "New Relic APM Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Redis Cache Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17')]", + "Resource Policy Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Scheduler Job Collections Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Manager (Legacy)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "SignalR/Web PubSub Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761')]", + "Site Recovery Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567')]", + "Site Recovery Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca')]", + "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')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Tag Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f')]", + "Traffic Manager Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", + "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]", + "Workbook Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad')]", + "Workbook Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d')]" + } + }, + "resources": [ + { + "copy": { + "name": "roleAssignment", + "count": "[length(parameters('principalIds'))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/activityLogAlerts/{0}', last(split(parameters('resourceId'), '/')))]", + "name": "[guid(resourceId('Microsoft.Insights/activityLogAlerts', last(split(parameters('resourceId'), '/'))), parameters('principalIds')[copyIndex()], parameters('roleDefinitionIdOrName'))]", + "properties": { + "description": "[parameters('description')]", + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]", + "principalId": "[parameters('principalIds')[copyIndex()]]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/activityLogAlerts', parameters('name'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the activity log alert." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the activity log alert." + }, + "value": "[resourceId('Microsoft.Insights/activityLogAlerts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the activity log alert was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference(resourceId('Microsoft.Insights/activityLogAlerts', parameters('name')), '2020-10-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', parameters('ActionGroupName'))]" + ] + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', parameters('ResourceGroupName'))]", + "roleAssignment_AutoAcctDesktopRead", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('c_LogContrib_{0}', split(parameters('LogAnalyticsWorkspaceResourceId'), '/')[4]))]", + "roleAssignment_Storage" + ] + } + ] +} \ No newline at end of file diff --git a/patterns/avd/avdCustomUi.json b/patterns/avd/avdCustomUi.json new file mode 100644 index 000000000..6abdccd11 --- /dev/null +++ b/patterns/avd/avdCustomUi.json @@ -0,0 +1,472 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2021-09-09/uiFormDefinition.schema.json", + "view": { + "kind": "Form", + "properties": { + "title": "Azure Virtual Desktop LZA: Create AVD Alerts", + "steps": [ + { + "name": "basics", + "label": "Basics", + "elements": [ + { + "name": "resourceScope", + "type": "Microsoft.Common.ResourceScope", + "instanceDetailsLabel": "AVD Alerts Deployment Details", + "subscription": { + "constraints": { + "validations": [] + } + } + }, + { + "name": "HostPoolsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/Microsoft.DesktopVirtualization/hostpools?api-version=2021-07-12')]" + } + }, + { + "name": "ResGroupsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/resourceGroups?api-version=2021-04-01')]" + } + }, + { + "name": "StorAcctsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/Microsoft.Storage/storageAccounts?api-version=2022-09-01')]" + } + }, + { + "name": "LogAnalyticsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/microsoft.operationalinsights/workspaces?api-version=2021-06-01')]" + } + } + ] + }, + { + "name": "AlertsConfig", + "label": "Alerts Configuration", + "elements": [ + { + "name": "optionCustomScriptLocation", + "type": "Microsoft.Common.CheckBox", + "label": "Using Custom Script Location", + "toolTip": "Selecting this will provide additional feilds for inputting a Blob Storage URL and Sas Token. Currently the deployment uses the Internet facing GitHub site. This is common when deploying in Air Gapped Clouds.", + "constraints": { + "required": false, + "validationMessage": "Selecting this will provide additional feilds for inputting a Blob Storage URL and Sas Token. Currently the deployment uses the Internet facing GitHub site." + } + }, + { + "name": "CustomScriptInfo", + "type": "Microsoft.Common.Section", + "label": "Custom Script Information", + "elements": [ + { + "name": "_ArtifactsLocation", + "type": "Microsoft.Common.TextBox", + "label": "Automation Account Scripts Location", + "subLabel": "", + "toolTip": "Blob Storage Location/ URL container with Get-HostPoolInfo.ps1 and Get-StorAcctInfov2.ps1 needed for deployment and automation account setup.", + "constraints": { + "required": false, + "regex": "", + "validationMessage": "" + }, + "visible": true + }, + { + "name": "_ArtifactsLocationSasToken", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Artifacts Location Sas Token", + "confirmPassword": "Confirm Sas Token" + }, + "toolTip": "SaS token if needed for script location. Be sure to include the ?sp= prefix.", + "constraints": { + "required": false, + "regex": "", + "validationMessage": "" + }, + "options": { + "hideConfirmation": true + }, + "visible": true + }, + { + "name": "infoMessageCustomScriptsLoc", + "type": "Microsoft.Common.InfoBox", + "visible": true, + "options": { + "text": "Be sure that the scripts Get-HostPoolInfo.ps1 and Get-StorAcctInfov2.ps1 are copied to the Selected Storage Location. IF using a Sas Token be sure to include the ?sp= prefix!", + "style": "Info" + } + } + ], + "visible": "[equals(steps('AlertsConfig').optionCustomScriptLocation, true)]" + }, + { + "name": "ResourceGroupStatus", + "type": "Microsoft.Common.OptionsGroup", + "label": "Use New or Existing Resource Group", + "toolTip": "This will be the Resource Group in which AVD Alerts resources will be deployed in.", + "constraints": { + "allowedValues": [ + { + "label": "New", + "value": "New" + }, + { + "label": "Existing", + "value": "Existing" + } + ], + "required": true + }, + "visible": true + }, + { + "name": "resourceGroupNameNew", + "type": "Microsoft.Common.TextBox", + "label": "Resource Group Name for AVD Alerts", + "subLabel": "", + "defaultValue": "rg-AVDAlerts", + "toolTip": "AVD Alerts Resource Group where alerts based resources will be deployed.", + "constraints": { + "required": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'New')]", + "regex": "", + "validationMessage": "" + }, + "visible": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'New')]" + }, + { + "name": "resourceGroupNameExisting", + "type": "Microsoft.Common.DropDown", + "label": "Existing Resource Group", + "multiselect": false, + "defaultValue": "", + "toolTip": "AVD Alerts Resource Group where alerts based resources will be deployed.", + "filter": true, + "filterPlaceholder": "Filter Resource Groups...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + }, + "visible": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'Existing')]" + }, + { + "name": "AlertNamePrefix", + "type": "Microsoft.Common.TextBox", + "label": "Alert Name Prefix", + "subLabel": "", + "defaultValue": "AVD", + "toolTip": "Alert Name Prefix of 1 to 5 characters. (Dash will be added after prefix for you.)", + "constraints": { + "required": true, + "regex": "^([a-zA-Z0-9_-]){1,5}$", + "validationMessage": "Must be 1-5 characters." + }, + "visible": true + }, + { + "name": "DistributionGroup", + "type": "Microsoft.Common.TextBox", + "label": "User Email or Distribution Group", + "subLabel": "", + "defaultValue": "", + "toolTip": "The Email Distribution Group that will receive email alerts for AVD.", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", + "validationMessage": "Email is not valid. Please re-enter." + }, + "visible": true + }, + { + "name": "Environment", + "type": "Microsoft.Common.DropDown", + "label": "Environment Type", + "defaultValue": "t", + "toolTip": "The environment is which these resources will be deployed, i.e. Test, Production, Development. This will yield a first letter desigation in some of the resource naming.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Development", + "value": "d" + }, + { + "label": "Production", + "value": "p" + }, + { + "label": "Test", + "value": "t" + } + ] + }, + "visible": true + }, + { + "name": "AutoResolveAlert", + "type": "Microsoft.Common.DropDown", + "label": "Allow Alerts to Auto-Resolve", + "defaultValue": "Yes", + "toolTip": "This option determines if the alert will automatically set the flag to resolved if a subsequent check is within the defined threshold.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + }, + "visible": true + }, + { + "name": "LogAnalyticsWorkspaceResource", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Insights Log Analytics Workspace", + "toolTip": "Log Analytics Workspace in which AVD Insigts and diagnostics data resides in.", + "resourceType": "Microsoft.OperationalInsights/workspaces", + "constraints": { + "required": true + }, + "infoMessages": [], + "visible": true + }, + { + "name": "optionVMMetrics", + "type": "Microsoft.Common.CheckBox", + "label": "VMs in separate Resource Group(s)", + "constraints": { + "required": false, + "validationMessage": "Selecting this will determine if multiple VM Metric based Alerts can be deployed given the scope for VM resources is per Resource Group." + } + }, + { + "name": "AVDResourceGroupId", + "type": "Microsoft.Common.DropDown", + "label": "AVD Resource Group", + "multiselect": false, + "defaultValue": "[]", + "selectAll": false, + "toolTip": "The Resource Group where all AVD resources are deployed to include VMs.", + "filter": true, + "filterPlaceholder": "Filter Resource Groups...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + "visible": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + { + "name": "HostPools", + "type": "Microsoft.Common.DropDown", + "label": "Host Pools", + "multiselect": true, + "selectAll": true, + "defaultValue": "[]", + "toolTip": "Select Host Pool(s) to configure Alerts for.", + "filter": true, + "filterPlaceholder": "Filter Host Pools...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').HostPoolsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + "visible": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + { + "name": "hostPoolInfo", + "type": "Microsoft.Common.EditableGrid", + "visible": "[if(steps('AlertsConfig').optionVMMetrics, true, false)]", + "ariaLabel": "Host Pool to VM Resource Mapping", + "label": "HostPool", + "constraints": { + "width": "Full", + "rows": { + "count": { + "min": 1, + "max": 20 + } + }, + "columns": [ + { + "id": "colHostPoolName", + "header": "Host Pool", + "width": "1fr", + "element": { + "type": "Microsoft.Common.DropDown", + "placeholder": "", + "constraints": { + "allowedValues": "[map(steps('basics').HostPoolsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + } + } + }, + { + "id": "colVMresGroup", + "header": "VM Resource Group", + "width": "1fr", + "element": { + "type": "Microsoft.Common.DropDown", + "placeholder": "", + "constraints": { + "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + } + } + } + ] + } + }, + { + "name": "optionAzFiles", + "type": "Microsoft.Common.CheckBox", + "label": "Configure Alerts for Azure Files", + "constraints": { + "required": false, + "validationMessage": "Selecting this option will prompt for Storage Account information." + } + }, + { + "name": "optionANFVolumes", + "type": "Microsoft.Common.CheckBox", + "label": "Configure Alerts for Azure NetApp Volumes", + "constraints": { + "required": false, + "validationMessage": "Selecting this option will prompt for NetApp information." + } + }, + { + "name": "AzFilesStorageSection", + "type": "Microsoft.Common.Section", + "label": "Azure Files Storage", + "elements": [ + { + "name": "StorageAccountResourceIds", + "type": "Microsoft.Common.DropDown", + "label": "AVD Related Storage Accounts", + "multiselect": true, + "selectAll": true, + "defaultValue": "[]", + "toolTip": "The Storage Accounts that are used for FSLogix or MSIX App attach.", + "filterPlaceholder": "Filter Storage Accounts...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').StorAcctsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "visible": "[steps('AlertsConfig').optionAzFiles]" + }, + { + "name": "ANFStorageSection", + "type": "Microsoft.Common.Section", + "label": "Azure NetApp Storage", + "elements": [ + { + "name": "ANFPool", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Azure NetApp Files Capacity Pool", + "toolTip": "Provide the ANF Capacity Pool housing the ANF Volumes for AVD.", + "resourceType": "Microsoft.NetApp/netAppAccounts/capacityPools", + "constraints": { + "required": true + }, + "visible": true + }, + { + "name": "ANFVolumesApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('AlertsConfig').ANFStorageSection.ANFPool.id, '/volumes?api-version=2022-09-01')]" + } + }, + { + "name": "ANFVolumeResourceIds", + "type": "Microsoft.Common.DropDown", + "label": "ANF Volumes used for AVD", + "multiselect": true, + "defaultValue": "[]", + "selectAll": true, + "toolTip": "The NetApp Volumes that are used for FSLogix or MSIX App attach.", + "filterPlaceholder": "Filter ANF Volumes...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('AlertsConfig').ANFStorageSection.ANFVolumesApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "visible": "[steps('AlertsConfig').optionANFVolumes]" + }, + { + "name": "Tags", + "type": "Microsoft.Common.TagsByResource", + "resources": [ + "Microsoft.Resources/resourceGroups", + "Microsoft.Automation/automationAccounts", + "Microsoft.Insights/diagnosticsettings", + "Microsoft.ManagedIdentity/userAssignedIdentities", + "Microsoft.Authorization/roleAssignments", + "Microsoft.Insights/actionGroups", + "Microsoft.Resources/deploymentScripts", + "Microsoft.Insights/metricAlerts", + "Microsoft.Insights/scheduledQueryRules", + "Microsoft.Automation/automationAccounts/runbooks", + "Microsoft.Logic/workflows" + ], + "visible": true + } + ] + } + ] + }, + "outputs": { + "parameters": { + "_ArtifactsLocation": "[steps('AlertsConfig').CustomScriptInfo._ArtifactsLocation]", + "_ArtifactsLocationSasToken": "[steps('AlertsConfig').CustomScriptInfo._ArtifactsLocationSasToken]", + "AlertNamePrefix": "[steps('AlertsConfig').AlertNamePrefix]", + "AllResourcesSameRG": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]", + "AutoResolveAlert": "[steps('AlertsConfig').AutoResolveAlert]", + "AVDResourceGroupId": "[steps('AlertsConfig').AVDResourceGroupId]", + "DistributionGroup": "[steps('AlertsConfig').DistributionGroup]", + "Environment": "[steps('AlertsConfig').Environment]", + "HostPoolInfo": "[steps('AlertsConfig').hostPoolInfo]", + "HostPools": "[steps('AlertsConfig').HostPools]", + "LogAnalyticsWorkspaceResourceId": "[steps('AlertsConfig').LogAnalyticsWorkspaceResource.id]", + "ResourceGroupName": "[if(equals(steps('AlertsConfig').ResourceGroupStatus, 'New'), steps('AlertsConfig').resourceGroupNameNew, last(split(steps('AlertsConfig').resourceGroupNameExisting, '/')))]", + "ResourceGroupStatus": "[steps('AlertsConfig').ResourceGroupStatus]", + "StorageAccountResourceIds": "[steps('AlertsConfig').AzFilesStorageSection.StorageAccountResourceIds]", + "ANFVolumeResourceIds": "[steps('AlertsConfig').ANFStorageSection.ANFVolumeResourceIds]", + "Tags": "[steps('AlertsConfig').Tags]" + }, + "kind": "Subscription", + "location": "[steps('basics').resourceScope.location.name]", + "subscriptionId": "[steps('basics').resourceScope.subscription.id]" + } + } +} \ No newline at end of file diff --git a/patterns/avd/examples/sample-pipeline.yml b/patterns/avd/examples/sample-pipeline.yml new file mode 100644 index 000000000..1906e8b65 --- /dev/null +++ b/patterns/avd/examples/sample-pipeline.yml @@ -0,0 +1,17 @@ +variables: + location: "northeurope" + ManagementGroupPrefix: "contoso" + serviceConnectionName: "AMBA-Service-Connection" + +pool: + vmImage: ubuntu-latest + +steps: + - task: AzureCLI@2 + displayName: "Deploy AMBA ARM template" + inputs: + azureSubscription: ${{ variables['serviceConnectionName'] }} + scriptType: bash + scriptLocation: inlineScript + inlineScript: | + az deployment mg create --template-uri https://raw.githubusercontent.com/Azure/azure-monitor-baseline-alerts/main/patterns/alz/alzArm.json --location $(location) --management-group-id $(ManagementGroupPrefix) --parameters ./patterns/alz/alzArm.param.json diff --git a/patterns/avd/examples/sample-workflow.yml b/patterns/avd/examples/sample-workflow.yml new file mode 100644 index 000000000..b70b44fae --- /dev/null +++ b/patterns/avd/examples/sample-workflow.yml @@ -0,0 +1,37 @@ +name: Deploy AMBA + +on: + workflow_dispatch: {} + +permissions: + id-token: write + contents: read + +env: + Location: "norwayeast" + ManagementGroupPrefix: "alz" + +jobs: + deploy_job: + runs-on: ubuntu-latest + environment: deploy + + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + - name: Az CLI Deploy AMBA ARM template + id: deploy_amba + shell: bash + run: | + az deployment mg create --template-uri https://raw.githubusercontent.com/Azure/azure-monitor-baseline-alerts/main/patterns/alz/alzArm.json --location ${{ env.Location }} --management-group-id ${{ env.ManagementGroupPrefix }} --parameters ./patterns/alz/alzArm.param.json diff --git a/patterns/avd/scripts/Get-HostPoolInfo.ps1 b/patterns/avd/scripts/Get-HostPoolInfo.ps1 new file mode 100644 index 000000000..b4a499adb --- /dev/null +++ b/patterns/avd/scripts/Get-HostPoolInfo.ps1 @@ -0,0 +1,68 @@ +<# +LAST UPDTATE: July 2023 +-- Added info for Personal Host Pool Info regarding need for alert where assigned but unhealthy +#> + + +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory)] + [string]$CloudEnvironment, + [Parameter(Mandatory)] + [string]$SubscriptionId +) + +Connect-AzAccount -Identity -Environment $CloudEnvironment | Out-Null + +$AVDHostPools = Get-AzWvdHostPool -SubscriptionId $SubscriptionId + +# $HostPoolInfoObj= @() +$Output = @() + +Foreach($AVDHostPool in $AVDHostPools){ + $HPPerUnhlthy = $null + $HPNumPerUnhlthy = $null + $HPPerHostUnhlthy = $null + $HPName = $AVDHostPool.Name + $HPResGroup = ($AVDHostPool.Id -split '/')[4] + $HPType = $AVDHostPool.HostPoolType + $HPMaxSessionLimit = $AVDHostPool.MaxSessionLimit + $HPSessionHosts = Get-AzWvdSessionHost -HostPoolName $HPName -ResourceGroupName $HPResGroup + $HPUsrSessions = Get-AzWvdUserSession -HostPoolName $HPName -ResourceGroupName $HPResGroup + $HPNumSessionHosts = $HPSessionHosts.count + $HPUsrSession = $HPUsrSessions.count + $HPUsrDisonnected = ($HPUsrSessions | Where-Object {$_.sessionState -eq "Disconnected" -AND $_.userPrincipalName -ne $null}).count + $HPUsrActive = ($HPUsrSessions | Where-Object {$_.sessionState -eq "Active" -AND $_.userPrincipalName -ne $null}).count + If($HPType -eq "Personal"){ + $HPPerUnhlthy = $HPSessionHosts | Where-Object {$_.AssignedUser -ne $null -AND $_.Status -ne "Available"} + $HPNumPerUnhlthy = $HPPerUnhlthy.count + $HostList = New-Object PSObject + foreach($item in $HPPerUnhlthy){ + $HostList | Add-Member -Type NoteProperty -Name SessionHost -Value ($item.name -split '/')[1] + $HostList | Add-Member -Type NoteProperty -Name AssignedUser -Value $item.AssignedUser + } + $HPPerHostUnhlthy = $HostList | ConvertTo-Json + } + + #Adding number of hosts available and allowing sessions, and Host Pool resource ID + $HP_ResID = $AVDHostPool.Id + $HPNumHostsAllowingSessions = (Get-AzWvdSessionHost -HostPoolName $HPName -ResourceGroupName $HPResGroup | where {$_.AllowNewSession}).count + $HPNumHostsAvailable = (Get-AzWvdSessionHost -HostPoolName $HPName -ResourceGroupName $HPResGroup | where {$_.Status -eq "Available"}).count + $HPNumHostsAvailableAndAllowingSessions = (Get-AzWvdSessionHost -HostPoolName $HPName -ResourceGroupName $HPResGroup | where {$_.Status -eq "Available" -and $_.AllowNewSession}).count + #Changing how we calculate available sessions to be based on number of hosts that are available and allowing sessions, instead of total number of hosts + +# Max allowed Sessions - Based on Total given unavailable may be on scaling plan + #$HPSessionsAvail = ($HPMaxSessionLimit * $HPNumSessionHosts)-$HPUsrSession + #if($HPUsrSession -ne 0) { + # $HPLoadPercent = ($HPUsrSession/($HPMaxSessionLimit * $HPNumSessionHosts))*100 + #} + $HPSessionsAvail = ($HPMaxSessionLimit * $HPNumHostsAvailableAndAllowingSessions)-$HPUsrSession + if($HPUsrSession -ne 0) { + $HPLoadPercent = ($HPUsrSession/($HPMaxSessionLimit * $HPNumHostsAvailableAndAllowingSessions))*100 + } + Else {$HPLoadPercent = 0} + $Output += $HPName + "|" + $HPResGroup + "|" + $HPType + "|" + $HPMaxSessionLimit + "|" + $HPNumSessionHosts + "|" + $HPUsrSession + "|" + $HPUsrDisonnected + "|" + $HPUsrActive + "|" + $HPSessionsAvail + "|" + $HPLoadPercent + "|" + $HPNumPerUnhlthy + "|" + $HPPerHostUnhlthy + "|" + $HP_ResID +} + +# $Output = ConvertTo-Json -InputObject $HostPoolInfoObj +Write-Output $Output \ No newline at end of file diff --git a/patterns/avd/scripts/Get-StorAcctInfo.ps1 b/patterns/avd/scripts/Get-StorAcctInfo.ps1 new file mode 100644 index 000000000..7d17aaaa0 --- /dev/null +++ b/patterns/avd/scripts/Get-StorAcctInfo.ps1 @@ -0,0 +1,69 @@ +# Deployed from resources.bicep +# Code for Runbook associated with Action Account deployment +# Collects Azure Files Storage data and writes output in following format: +# AzFiles, Subscription ,RG ,StorAcct ,Share ,Quota ,GB Used ,%Available + +<# +//Kusto Query for Log Analtyics +AzureDiagnostics +| where Category has "JobStreams" +| where StreamType_s has "Output" +| extend Results=split(ResultDescription,',') +#> + +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory)] + [string]$CloudEnvironment, + [Parameter(Mandatory)] + [array]$StorageAccountResourceIDs +) + +Connect-AzAccount -Identity -Environment $CloudEnvironment | Out-Null +Import-Module -Name 'Az.Accounts' +Import-Module -Name 'Az.Storage' + +$SubName = (Get-azSubscription -SubscriptionId ($StorageAccountResourceIDs -split '/')[2]).Name + +# Foreach storage account +Foreach ($storageAcct in $storageAccountResourceIDs) { + + $resourceGroup = ($storageAcct -split '/')[4] + $storageAcctName = ($storageAcct -split '/')[8] + #Write-Host "Working on Storage:" $storageAcctName "in" $resourceGroup + + # $shares = Get-AzStorageShare -ResourceGroupName $resourceGroup -StorageAccountName $storageAcctName -Name 'profiles' -GetShareUsage + $shares = Get-AzRmStorageShare -ResourceGroupName $ResourceGroup -StorageAccountName $storageAcctName + + # Foreach Share + Foreach ($share in $shares) { + $shareName = $share.Name + $share = Get-AzRmStorageShare -ResourceGroupName $ResourceGroup -StorageAccountName $storageAcctName -Name $shareName -GetShareUsage + #Write-Host "Share: " $shareName + $shareQuota = $share.QuotaGiB #GB + $shareUsageInGB = $share.ShareUsageBytes / 1073741824 # Bytes to GB + + $RemainingPercent = 100 - ($shareUsageInGB / $shareQuota) + #Write-Host "..." $shareUsageInGB "of" $shareQuota "GB used" + #Write-Host "..." $RemainingPercent "% Available" + # Add file share resource Id + $shareResourceId = $share.Id + # Compile results + # AzFiles / Subscription / RG / StorAcct / Share / Quota / GB Used / %Available + $Data = @('AzFiles', $SubName, $resourceGroup, $storageAcctName, $shareName, $shareQuota.ToString(), $shareUsageInGB.ToString(), $RemainingPercent.ToString(), $shareResourceId) + $i = 0 + ForEach ($Item in $Data) { + If ($i -ne $Data.Length - 1) { + # Ensure we don't add the trailing comma if last item + $Output += $Item + ',' + $i += 1 + } + else { $Output += $Item } + } + + Write-Output $Output + $Output = $Null + $Data = $Null + } # end for each share + +} # end for each storage acct \ No newline at end of file diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/.test/common/deploy.test.bicep new file mode 100644 index 000000000..3991a8f27 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/.test/common/deploy.test.bicep @@ -0,0 +1,43 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.locks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'alcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + level: 'CanNotDelete' + resourceGroupName: resourceGroup.name + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/deploy.bicep new file mode 100644 index 000000000..9306aeec8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/deploy.bicep @@ -0,0 +1,71 @@ +targetScope = 'subscription' + +@allowed([ + 'CanNotDelete' + 'ReadOnly' +]) +@description('Required. Set lock level.') +param level string + +@description('Optional. The decription attached to the lock.') +param notes string = level == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Name of the Resource Group to assign the lock to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided lock to the resource group.') +param resourceGroupName string = '' + +@description('Optional. Subscription ID of the subscription to assign the lock to. If not provided, will use the current scope for deployment. If no resource group name is provided, the module deploys at subscription level, therefore assigns the provided locks to the subscription.') +param subscriptionId string = subscription().id + +@description('Optional. Location for all resources.') +param location string = deployment().location + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module lock_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-Lock-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: '${subscription().displayName}-${level}-lock' + level: level + notes: notes + // owners: owners // Not intended to be applied by users (ref https://github.com/Azure/azure-cli/issues/22528) + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module lock_rg 'resourceGroup/deploy.bicep' = if (!empty(subscriptionId) && !empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-Lock-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: '${resourceGroupName}-${level}-lock' + level: level + notes: notes + // owners: owners // Not intended to be applied by users (ref https://github.com/Azure/azure-cli/issues/22528) + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The name of the lock.') +output name string = empty(resourceGroupName) ? lock_sub.outputs.name : lock_rg.outputs.name + +@description('The resource ID of the lock.') +output resourceId string = empty(resourceGroupName) ? lock_sub.outputs.resourceId : lock_rg.outputs.resourceId + +@sys.description('The scope this lock applies to.') +output scope string = empty(resourceGroupName) ? lock_sub.outputs.scope : lock_rg.outputs.scope diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/readme.md new file mode 100644 index 000000000..7278a7ccf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/readme.md @@ -0,0 +1,108 @@ +# Authorization Locks `[Microsoft.Authorization/locks]` + +This module deploys Authorization Locks. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `level` | string | `[CanNotDelete, ReadOnly]` | Set lock level. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location for all resources. | +| `notes` | string | `[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]` | The decription attached to the lock. | +| `resourceGroupName` | string | `''` | Name of the Resource Group to assign the lock to. If Resource Group name is provided, and Subscription ID is provided, the module deploys at resource group level, therefore assigns the provided lock to the resource group. | +| `subscriptionId` | string | `[subscription().id]` | Subscription ID of the subscription to assign the lock to. If not provided, will use the current scope for deployment. If no resource group name is provided, the module deploys at subscription level, therefore assigns the provided locks to the subscription. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the lock. | +| `resourceId` | string | The resource ID of the lock. | +| `scope` | string | The scope this lock applies to. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module locks './Microsoft.Authorization/locks/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-alcom' + params: { + // Required parameters + level: 'CanNotDelete' + // Non-required parameters + enableDefaultTelemetry: '' + 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 + "level": { + "value": "CanNotDelete" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/deploy.bicep new file mode 100644 index 000000000..aaae7650a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/deploy.bicep @@ -0,0 +1,50 @@ +targetScope = 'resourceGroup' + +@description('Optional. The name of the lock.') +param name string = '${level}-lock' + +@allowed([ + 'CanNotDelete' + 'ReadOnly' +]) +@description('Required. Set lock level.') +param level string + +@description('Optional. The decription attached to the lock.') +param notes string = level == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource lock 'Microsoft.Authorization/locks@2020-05-01' = { + name: name + properties: { + level: level + notes: notes + // owners: owners // Not intended to be applied by users (ref https://github.com/Azure/azure-cli/issues/22528) + } +} + +@description('The name of the lock.') +output name string = lock.name + +@description('The resource ID of the lock.') +output resourceId string = lock.id + +@description('The name of the resource group name the lock was applied to.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The scope this lock applies to.') +output scope string = resourceGroup().id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/readme.md new file mode 100644 index 000000000..b6e89a621 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/readme.md @@ -0,0 +1,46 @@ +# Authorization Locks on Resource Group level `[Microsoft.Authorization/locks/resourceGroup]` + +This module deploys Authorization Locks on Resource Group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `level` | string | `[CanNotDelete, ReadOnly]` | Set lock level. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `name` | string | `[format('{0}-lock', parameters('level'))]` | The name of the lock. | +| `notes` | string | `[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]` | The decription attached to the lock. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the lock. | +| `resourceGroupName` | string | The name of the resource group name the lock was applied to. | +| `resourceId` | string | The resource ID of the lock. | +| `scope` | string | The scope this lock applies to. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/deploy.bicep new file mode 100644 index 000000000..41e643904 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/deploy.bicep @@ -0,0 +1,50 @@ +targetScope = 'subscription' + +@description('Optional. The name of the lock.') +param name string = '${level}-lock' + +@allowed([ + 'CanNotDelete' + 'ReadOnly' +]) +@description('Required. Set lock level.') +param level string + +@description('Optional. The decription attached to the lock.') +param notes string = level == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource lock 'Microsoft.Authorization/locks@2020-05-01' = { + name: name + properties: { + level: level + notes: notes + // owners: owners // Not intended to be applied by users (ref https://github.com/Azure/azure-cli/issues/22528) + } +} + +@description('The name of the lock.') +output name string = lock.name + +@description('The resource ID of the lock.') +output resourceId string = lock.id + +@description('The subscription name the lock was deployed into.') +output subscriptionName string = subscription().displayName + +@sys.description('The scope this lock applies to.') +output scope string = subscription().id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/readme.md new file mode 100644 index 000000000..d8cd8587d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/readme.md @@ -0,0 +1,46 @@ +# Authorization Locks on Subscription level `[Microsoft.Authorization/locks/subscription]` + +This module deploys Authorization Locks on Subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `level` | string | `[CanNotDelete, ReadOnly]` | Set lock level. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `name` | string | `[format('{0}-lock', parameters('level'))]` | The name of the lock. | +| `notes` | string | `[if(equals(parameters('level'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot modify the resource or child resources.')]` | The decription attached to the lock. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the lock. | +| `resourceId` | string | The resource ID of the lock. | +| `scope` | string | The scope this lock applies to. | +| `subscriptionName` | string | The subscription name the lock was deployed into. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/locks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.common/deploy.test.bicep new file mode 100644 index 000000000..bf1bdb750 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.common/deploy.test.bicep @@ -0,0 +1,90 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'apamgcom' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + 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: location + managementGroupId: last(split(managementGroup().id, '/')) + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/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' + ] + } + ] + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.min/deploy.test.bicep new file mode 100644 index 000000000..0f1332f0e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/mg.min/deploy.test.bicep @@ -0,0 +1,24 @@ +targetScope = 'managementGroup' + +// ========== // +// 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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/dependencies.bicep new file mode 100644 index 000000000..f4151d61c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/deploy.test.bicep new file mode 100644 index 000000000..77ee2e04d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.common/deploy.test.bicep @@ -0,0 +1,117 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.policyassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apargcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + keyVaultName: 'dep-<>-kv-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + 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: location + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + nestedDependencies.outputs.keyVaultResourceId + ] + parameters: { + enableCollectionOfSqlQueriesForSecurityResearch: { + value: false + } + effect: { + value: 'Disabled' + } + } + resourceGroupName: resourceGroup.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: subscription().subscriptionId + userAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.min/deploy.test.bicep new file mode 100644 index 000000000..4a1151b52 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/rg.min/deploy.test.bicep @@ -0,0 +1,44 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.policyassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/dependencies.bicep new file mode 100644 index 000000000..f17c563bb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/deploy.test.bicep new file mode 100644 index 000000000..a2c7bc666 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.common/deploy.test.bicep @@ -0,0 +1,114 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.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 = 'apasubcom' + +@description('Optional. The location to deploy resources to.') +param location string = deployment().location + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + 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: location + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/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: subscription().subscriptionId + userAssignedIdentityId: nestedDependencies.outputs.managedIdentityResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.min/deploy.test.bicep new file mode 100644 index 000000000..4112d4198 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/.test/sub.min/deploy.test.bicep @@ -0,0 +1,25 @@ +targetScope = 'subscription' + +// ========== // +// 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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/deploy.bicep new file mode 100644 index 000000000..3a0475330 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/deploy.bicep @@ -0,0 +1,167 @@ +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 telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policyAssignment_mg 'managementGroup/deploy.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 : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyAssignment_sub 'subscription/deploy.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 : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyAssignment_rg 'resourceGroup/deploy.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 : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep new file mode 100644 index 000000000..1c8271c3e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/deploy.bicep @@ -0,0 +1,124 @@ +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 = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +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 = identity == 'SystemAssigned' ? 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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/readme.md new file mode 100644 index 000000000..c2533484e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/readme.md @@ -0,0 +1,60 @@ +# Policy Assignment on Management Group level `[Microsoft.Authorization/policyAssignments/managementGroup]` + +With this module you can perform policy assignments on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 24 characters for management group scope. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[deployment().location]` | | Location for all resources. | +| `managementGroupId` | string | `[managementGroup().name]` | | 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` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `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` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `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` | 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. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +## Outputs + +| Output Name | 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_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/readme.md new file mode 100644 index 000000000..1e0633436 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/readme.md @@ -0,0 +1,953 @@ +# Policy Assignments `[Microsoft.Authorization/policyAssignments]` + +With this module you can perform policy assignments across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `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` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[deployment().location]` | | Location for all resources. | +| `managementGroupId` | string | `[managementGroup().name]` | | 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` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `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` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `resourceGroupName` | string | `''` | | The Target Scope for the Policy. The name of the resource group for the policy assignment. | +| `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` | 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` | string | `''` | | The Target Scope for the Policy. The subscription ID of the subscription for the policy assignment. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +resourceGroupName: 'target-resourceGroup' +``` + +
+

+ +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policyassignment 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policyassignments.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policyassignment 'yourpath/modules/Microsoft.Authorization.policyAssignments/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | 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_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg.Common

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-apamgcom' + params: { + // Required parameters + name: '<>apamgcom001' + 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' + enableDefaultTelemetry: '' + enforcementMode: 'DoNotEnforce' + identity: 'SystemAssigned' + location: '' + managementGroupId: '' + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/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": "<>apamgcom001" + }, + "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" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "SystemAssigned" + }, + "location": { + "value": "" + }, + "managementGroupId": { + "value": "" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/<>/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 2: Mg.Min

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apamgmin' + params: { + // Required parameters + name: '<>apamgmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 3: Rg.Common

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apargcom' + params: { + // Required parameters + name: '<>apargcom001' + 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' + enableDefaultTelemetry: '' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: '' + metadata: { + 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": "<>apargcom001" + }, + "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" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "" + }, + "metadata": { + "value": { + "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 4: Rg.Min

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apargmin' + params: { + // Required parameters + name: '<>apargmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + enableDefaultTelemetry: '' + 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 + "enableDefaultTelemetry": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 5: Sub.Common

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apasubcom' + params: { + // Required parameters + name: '<>apasubcom001' + 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' + enableDefaultTelemetry: '' + enforcementMode: 'DoNotEnforce' + identity: 'UserAssigned' + location: '' + metadata: { + category: 'Security' + version: '1.0' + } + nonComplianceMessages: [ + { + message: 'Violated Policy Assignment - This is a Non Compliance Message' + } + ] + notScopes: [ + '/subscriptions/<>/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": "<>apasubcom001" + }, + "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" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enforcementMode": { + "value": "DoNotEnforce" + }, + "identity": { + "value": "UserAssigned" + }, + "location": { + "value": "" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1.0" + } + }, + "nonComplianceMessages": { + "value": [ + { + "message": "Violated Policy Assignment - This is a Non Compliance Message" + } + ] + }, + "notScopes": { + "value": [ + "/subscriptions/<>/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": "" + } + } +} +``` + +
+

+ +

Example 6: Sub.Min

+ +
+ +via Bicep module + +```bicep +module policyAssignments './Microsoft.Authorization/policyAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apasubmin' + params: { + // Required parameters + name: '<>apasubmin001' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d' + // Non-required parameters + enableDefaultTelemetry: '' + 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 + "enableDefaultTelemetry": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep new file mode 100644 index 000000000..c43e30faf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/deploy.bicep @@ -0,0 +1,129 @@ +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 + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +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 = identity == 'SystemAssigned' ? 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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md new file mode 100644 index 000000000..ad4e8bb16 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/readme.md @@ -0,0 +1,62 @@ +# Policy Assignment on Resource Group level `[Microsoft.Authorization/policyAssignments/resourceGroup]` + +With this module you can perform policy assignments on a resource group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 64 characters for resource group scope. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `metadata` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `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` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `resourceGroupName` | string | `[resourceGroup().name]` | | 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. | +| `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` | 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` | string | `[subscription().subscriptionId]` | | 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. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | Policy Assignment Name. | +| `principalId` | string | Policy Assignment principal ID. | +| `resourceGroupName` | string | The name of the resource group the policy was assigned to. | +| `resourceId` | string | Policy Assignment resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep new file mode 100644 index 000000000..57f7e4f88 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/deploy.bicep @@ -0,0 +1,124 @@ +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 + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var identityVar = identity == 'SystemAssigned' ? { + type: identity +} : identity == 'UserAssigned' ? { + type: identity + userAssignedIdentities: { + '${userAssignedIdentityId}': {} + } +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +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 = identity == 'SystemAssigned' ? 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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/readme.md new file mode 100644 index 000000000..2f03779af --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/readme.md @@ -0,0 +1,60 @@ +# Policy Assignment on Subscription level `[Microsoft.Authorization/policyAssignments/subscription]` + +With this module you can perform policy assignments on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy assignment. Maximum length is 64 characters for subscription scope. | +| `policyDefinitionId` | string | Specifies the ID of the policy definition or policy set definition being assigned. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | This message will be part of response in case of policy violation. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enforcementMode` | string | `'Default'` | `[Default, DoNotEnforce]` | The policy assignment enforcement mode. Possible values are Default and DoNotEnforce. - Default or DoNotEnforce. | +| `identity` | string | `'SystemAssigned'` | `[None, SystemAssigned, UserAssigned]` | The managed identity associated with the policy assignment. Policy assignments must include a resource identity when assigning 'Modify' policy definitions. | +| `location` | string | `[deployment().location]` | | Location for all resources. | +| `metadata` | object | `{object}` | | The policy assignment metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `nonComplianceMessages` | array | `[]` | | The messages that describe why a resource is non-compliant with the policy. | +| `notScopes` | array | `[]` | | The policy excluded scopes. | +| `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` | object | `{object}` | | Parameters for the policy assignment if needed. | +| `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` | 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` | string | `[subscription().subscriptionId]` | | 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. | +| `userAssignedIdentityId` | string | `''` | | The Resource ID for the user assigned identity to assign to the policy assignment. | + + +## Outputs + +| Output Name | 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_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyAssignments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.common/deploy.test.bicep new file mode 100644 index 000000000..66b0d22b6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.common/deploy.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'apdmgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.Resources/subscriptions' + field: 'type' + } + { + exists: 'false' + field: '[concat(\'tags[\', parameters(\'tagName\'), \']\')]' + } + ] + } + then: { + details: { + operations: [ + { + field: '[concat(\'tags[\', parameters(\'tagName\'), \']\')]' + operation: 'add' + value: '[parameters(\'tagValue\')]' + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ] + } + effect: 'modify' + } + } + description: '[Description] This policy definition is deployed at the management group scope' + displayName: '[DisplayName] This policy definition is deployed at the management group scope' + metadata: { + category: 'Security' + } + parameters: { + tagName: { + metadata: { + description: 'Name of the tag such as \'environment\'' + displayName: 'Tag Name' + } + type: 'String' + } + tagValue: { + metadata: { + description: 'Value of the tag such as \'environment\'' + displayName: 'Tag Value' + } + type: 'String' + } + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.min/deploy.test.bicep new file mode 100644 index 000000000..266303816 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/mg.min/deploy.test.bicep @@ -0,0 +1,45 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'apdmgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.common/deploy.test.bicep new file mode 100644 index 000000000..b232e9463 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.common/deploy.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'apdsubcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.Resources/subscriptions' + field: 'type' + } + { + exists: 'false' + field: '[concat(\'tags[\', parameters(\'tagName\'), \']\')]' + } + ] + } + then: { + details: { + operations: [ + { + field: '[concat(\'tags[\', parameters(\'tagName\'), \']\')]' + operation: 'add' + value: '[parameters(\'tagValue\')]' + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ] + } + effect: 'modify' + } + } + description: '[Description] This policy definition is deployed at subscription scope' + displayName: '[DisplayName] This policy definition is deployed at subscription scope' + metadata: { + category: 'Security' + } + parameters: { + tagName: { + metadata: { + description: 'Name of the tag such as \'environment\'' + displayName: 'Tag Name' + } + type: 'String' + } + tagValue: { + metadata: { + description: 'Value of the tag such as \'production\'' + displayName: 'Tag Value' + } + type: 'String' + } + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.min/deploy.test.bicep new file mode 100644 index 000000000..e1a7defaf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/.test/sub.min/deploy.test.bicep @@ -0,0 +1,45 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'apdsubmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/deploy.bicep new file mode 100644 index 000000000..30f1c1b4d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/deploy.bicep @@ -0,0 +1,100 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy definition. Maximum length is 64 characters for management group scope and subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy definition. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The policy definition description.') +param description string = '' + +@sys.description('Optional. The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data.') +@allowed([ + 'All' + 'Indexed' + 'Microsoft.KeyVault.Data' + 'Microsoft.ContainerService.Data' + 'Microsoft.Kubernetes.Data' + 'Microsoft.Network.Data' +]) +param mode string = 'All' + +@sys.description('Optional. The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy definition parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Required. The Policy Rule details for the Policy Definition.') +param policyRule object + +@sys.description('Optional. The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID of the subscription (Scope). Cannot be used with managementGroupId.') +param subscriptionId string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policyDefinition_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyDefinition-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + mode: mode + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyRule: policyRule + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyDefinition_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyDefinition-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + mode: mode + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyRule: policyRule + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Definition Name.') +output name string = empty(subscriptionId) ? policyDefinition_mg.outputs.name : policyDefinition_sub.outputs.name + +@sys.description('Policy Definition resource ID.') +output resourceId string = empty(subscriptionId) ? policyDefinition_mg.outputs.resourceId : policyDefinition_sub.outputs.resourceId + +@sys.description('Policy Definition Role Definition IDs.') +output roleDefinitionIds array = empty(subscriptionId) ? policyDefinition_mg.outputs.roleDefinitionIds : policyDefinition_sub.outputs.roleDefinitionIds diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep new file mode 100644 index 000000000..3765a539f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/deploy.bicep @@ -0,0 +1,73 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy definition. Maximum length is 64 characters.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy definition. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The policy definition description.') +param description string = '' + +@sys.description('Optional. The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data.') +@allowed([ + 'All' + 'Indexed' + 'Microsoft.KeyVault.Data' + 'Microsoft.ContainerService.Data' + 'Microsoft.Kubernetes.Data' + 'Microsoft.Network.Data' +]) +param mode string = 'All' + +@sys.description('Optional. The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy definition parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Required. The Policy Rule details for the Policy Definition.') +param policyRule object + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + mode: mode + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyRule: policyRule + } +} + +@sys.description('Policy Definition Name.') +output name string = policyDefinition.name + +@sys.description('Policy Definition resource ID.') +output resourceId string = policyDefinition.id + +@sys.description('Policy Definition Role Definition IDs.') +output roleDefinitionIds array = (contains(policyDefinition.properties.policyRule.then, 'details') ? ((contains(policyDefinition.properties.policyRule.then.details, 'roleDefinitionIds') ? policyDefinition.properties.policyRule.then.details.roleDefinitionIds : [])) : []) diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md new file mode 100644 index 000000000..546cc421d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/readme.md @@ -0,0 +1,50 @@ +# Policy Definitions on Management Group level `[Microsoft.Authorization/policyDefinitions/managementGroup]` + +With this module you can create policy definitions on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyDefinitions` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy definition. Maximum length is 64 characters. | +| `policyRule` | object | The Policy Rule details for the Policy Definition. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The policy definition description. | +| `displayName` | string | `''` | | The display name of the policy definition. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `metadata` | object | `{object}` | | The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `mode` | string | `'All'` | `[All, Indexed, Microsoft.ContainerService.Data, Microsoft.KeyVault.Data, Microsoft.Kubernetes.Data, Microsoft.Network.Data]` | The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data. | +| `parameters` | object | `{object}` | | The policy definition parameters that can be used in policy definition references. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Definition Name. | +| `resourceId` | string | Policy Definition resource ID. | +| `roleDefinitionIds` | array | Policy Definition Role Definition IDs. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/readme.md new file mode 100644 index 000000000..a77bbe9e6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/readme.md @@ -0,0 +1,635 @@ +# Policy Definitions `[Microsoft.Authorization/policyDefinitions]` + +With this module you can create policy definitions across the management group or subscription scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyDefinitions` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy definition. Maximum length is 64 characters for management group scope and subscription scope. | +| `policyRule` | object | The Policy Rule details for the Policy Definition. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The policy definition description. | +| `displayName` | string | `''` | | The display name of the policy definition. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `mode` | string | `'All'` | `[All, Indexed, Microsoft.ContainerService.Data, Microsoft.KeyVault.Data, Microsoft.Kubernetes.Data, Microsoft.Network.Data]` | The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data. | +| `parameters` | object | `{object}` | | The policy definition parameters that can be used in policy definition references. | +| `subscriptionId` | string | `''` | | The subscription ID of the subscription (Scope). Cannot be used with managementGroupId. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policydefinition 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policydefinitions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policydefinition 'yourpath/modules/Microsoft.Authorization.policyDefinitions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Definition Name. | +| `resourceId` | string | Policy Definition resource ID. | +| `roleDefinitionIds` | array | Policy Definition Role Definition IDs. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg.Common

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apdmgcom' + params: { + // Required parameters + name: '<>apdmgcom001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.Resources/subscriptions' + field: 'type' + } + { + exists: 'false' + field: '[concat(\'tags[\' parameters(\'tagName\') \']\')]' + } + ] + } + then: { + details: { + operations: [ + { + field: '[concat(\'tags[\' parameters(\'tagName\') \']\')]' + operation: 'add' + value: '[parameters(\'tagValue\')]' + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ] + } + effect: 'modify' + } + } + // Non-required parameters + description: '[Description] This policy definition is deployed at the management group scope' + displayName: '[DisplayName] This policy definition is deployed at the management group scope' + enableDefaultTelemetry: '' + metadata: { + category: 'Security' + } + parameters: { + tagName: { + metadata: { + description: 'Name of the tag such as \'environment\'' + displayName: 'Tag Name' + } + type: 'String' + } + tagValue: { + metadata: { + description: 'Value of the tag such as \'environment\'' + displayName: 'Tag Value' + } + type: 'String' + } + } + } +} +``` + +
+

+ +

+ +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": "<>apdmgcom001" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.Resources/subscriptions", + "field": "type" + }, + { + "exists": "false", + "field": "[concat(\"tags[\", parameters(\"tagName\"), \"]\")]" + } + ] + }, + "then": { + "details": { + "operations": [ + { + "field": "[concat(\"tags[\", parameters(\"tagName\"), \"]\")]", + "operation": "add", + "value": "[parameters(\"tagValue\")]" + } + ], + "roleDefinitionIds": [ + "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f" + ] + }, + "effect": "modify" + } + } + }, + // Non-required parameters + "description": { + "value": "[Description] This policy definition is deployed at the management group scope" + }, + "displayName": { + "value": "[DisplayName] This policy definition is deployed at the management group scope" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "parameters": { + "value": { + "tagName": { + "metadata": { + "description": "Name of the tag such as \"environment\"", + "displayName": "Tag Name" + }, + "type": "String" + }, + "tagValue": { + "metadata": { + "description": "Value of the tag such as \"environment\"", + "displayName": "Tag Value" + }, + "type": "String" + } + } + } + } +} +``` + +
+

+ +

Example 2: Mg.Min

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apdmgmin' + params: { + // Required parameters + name: '<>apdmgmin001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + // Non-required parameters + enableDefaultTelemetry: '' + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} +``` + +
+

+ +

+ +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": "<>apdmgmin001" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.KeyVault/vaults", + "field": "type" + } + ] + }, + "then": { + "effect": "[parameters(\"effect\")]" + } + } + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "parameters": { + "value": { + "effect": { + "allowedValues": [ + "Audit" + ], + "defaultValue": "Audit", + "type": "String" + } + } + } + } +} +``` + +
+

+ +

Example 3: Sub.Common

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apdsubcom' + params: { + // Required parameters + name: '<>apdsubcom001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.Resources/subscriptions' + field: 'type' + } + { + exists: 'false' + field: '[concat(\'tags[\' parameters(\'tagName\') \']\')]' + } + ] + } + then: { + details: { + operations: [ + { + field: '[concat(\'tags[\' parameters(\'tagName\') \']\')]' + operation: 'add' + value: '[parameters(\'tagValue\')]' + } + ] + roleDefinitionIds: [ + '/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + ] + } + effect: 'modify' + } + } + // Non-required parameters + description: '[Description] This policy definition is deployed at subscription scope' + displayName: '[DisplayName] This policy definition is deployed at subscription scope' + enableDefaultTelemetry: '' + metadata: { + category: 'Security' + } + parameters: { + tagName: { + metadata: { + description: 'Name of the tag such as \'environment\'' + displayName: 'Tag Name' + } + type: 'String' + } + tagValue: { + metadata: { + description: 'Value of the tag such as \'production\'' + displayName: 'Tag Value' + } + type: 'String' + } + } + } +} +``` + +
+

+ +

+ +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": "<>apdsubcom001" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.Resources/subscriptions", + "field": "type" + }, + { + "exists": "false", + "field": "[concat(\"tags[\", parameters(\"tagName\"), \"]\")]" + } + ] + }, + "then": { + "details": { + "operations": [ + { + "field": "[concat(\"tags[\", parameters(\"tagName\"), \"]\")]", + "operation": "add", + "value": "[parameters(\"tagValue\")]" + } + ], + "roleDefinitionIds": [ + "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f" + ] + }, + "effect": "modify" + } + } + }, + // Non-required parameters + "description": { + "value": "[Description] This policy definition is deployed at subscription scope" + }, + "displayName": { + "value": "[DisplayName] This policy definition is deployed at subscription scope" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "parameters": { + "value": { + "tagName": { + "metadata": { + "description": "Name of the tag such as \"environment\"", + "displayName": "Tag Name" + }, + "type": "String" + }, + "tagValue": { + "metadata": { + "description": "Value of the tag such as \"production\"", + "displayName": "Tag Value" + }, + "type": "String" + } + } + } + } +} +``` + +
+

+ +

Example 4: Sub.Min

+ +
+ +via Bicep module + +```bicep +module policyDefinitions './Microsoft.Authorization/policyDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apdsubmin' + params: { + // Required parameters + name: '<>apdsubmin001' + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + // Non-required parameters + enableDefaultTelemetry: '' + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} +``` + +
+

+ +

+ +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": "<>apdsubmin001" + }, + "policyRule": { + "value": { + "if": { + "allOf": [ + { + "equals": "Microsoft.KeyVault/vaults", + "field": "type" + } + ] + }, + "then": { + "effect": "[parameters(\"effect\")]" + } + } + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "parameters": { + "value": { + "effect": { + "allowedValues": [ + "Audit" + ], + "defaultValue": "Audit", + "type": "String" + } + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep new file mode 100644 index 000000000..6af7b0bff --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/deploy.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy definition. Maximum length is 64 characters.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy definition. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The policy definition description.') +param description string = '' + +@sys.description('Optional. The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data.') +@allowed([ + 'All' + 'Indexed' + 'Microsoft.KeyVault.Data' + 'Microsoft.ContainerService.Data' + 'Microsoft.Kubernetes.Data' + 'Microsoft.Network.Data' +]) +param mode string = 'All' + +@sys.description('Optional. The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy definition parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Required. The Policy Rule details for the Policy Definition.') +param policyRule object + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + mode: mode + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyRule: policyRule + } +} + +@sys.description('Policy Definition Name.') +output name string = policyDefinition.name + +@sys.description('Policy Definition resource ID.') +output resourceId string = policyDefinition.id + +@sys.description('Policy Definition Role Definition IDs.') +output roleDefinitionIds array = (contains(policyDefinition.properties.policyRule.then, 'details') ? ((contains(policyDefinition.properties.policyRule.then.details, 'roleDefinitionIds') ? policyDefinition.properties.policyRule.then.details.roleDefinitionIds : [])) : []) diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/readme.md new file mode 100644 index 000000000..b290507f7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/readme.md @@ -0,0 +1,50 @@ +# Policy Definitions on Subscription level `[Microsoft.Authorization/policyDefinitions/subscription]` + +With this module you can create policy definitions on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyDefinitions` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policyDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy definition. Maximum length is 64 characters. | +| `policyRule` | object | The Policy Rule details for the Policy Definition. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `description` | string | `''` | | The policy definition description. | +| `displayName` | string | `''` | | The display name of the policy definition. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `metadata` | object | `{object}` | | The policy Definition metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `mode` | string | `'All'` | `[All, Indexed, Microsoft.ContainerService.Data, Microsoft.KeyVault.Data, Microsoft.Kubernetes.Data, Microsoft.Network.Data]` | The policy definition mode. Default is All, Some examples are All, Indexed, Microsoft.KeyVault.Data. | +| `parameters` | object | `{object}` | | The policy definition parameters that can be used in policy definition references. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Definition Name. | +| `resourceId` | string | Policy Definition resource ID. | +| `roleDefinitionIds` | array | Policy Definition Role Definition IDs. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.common/deploy.test.bicep new file mode 100644 index 000000000..c0e074296 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.common/deploy.test.bicep @@ -0,0 +1,112 @@ +targetScope = 'managementGroup' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apemgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= + +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: 'dep-<>-polDef-AuditKvlt-${serviceShort}' + properties: { + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} + +resource policySet 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: 'dep-<>-polSet-${serviceShort}' + properties: { + policyDefinitions: [ + { + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: policyDefinition.id + policyDefinitionReferenceId: policyDefinition.name + } + ] + } +} + +resource policySetAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: 'dep-<>-psa-${serviceShort}' + location: location + properties: { + displayName: 'Test case assignment' + policyDefinitionId: policySet.id + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyAssignmentId: policySetAssignment.id + displayName: '[Display Name] policy exempt (management group scope)' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + assignmentScopeValidation: 'Default' + description: 'My description' + resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + kind: 'resourceLocation' + in: [ + 'westcentralus' + ] + } + ] + } + ] + policyDefinitionReferenceIds: [ + policySet.properties.policyDefinitions[0].policyDefinitionReferenceId + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.min/deploy.test.bicep new file mode 100644 index 000000000..43bb33641 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/mg.min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'managementGroup' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apemgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: 'dep-<>-${serviceShort}-rgloc' + location: location + properties: { + displayName: '[Depedency] Audit resource location matches resource group location (management group scope)' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/0a914e76-4921-4c19-b460-a2d36003525a' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyAssignmentId: policyAssignment.id + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.common/deploy.test.bicep new file mode 100644 index 000000000..1d77acd98 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.common/deploy.test.bicep @@ -0,0 +1,121 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.policyexemptions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apergcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: 'dep-<>-polDef-AuditKvlt-${serviceShort}' + properties: { + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} + +resource policySet 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: 'dep-<>-polSet-${serviceShort}' + properties: { + policyDefinitions: [ + { + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: policyDefinition.id + policyDefinitionReferenceId: policyDefinition.name + } + ] + } +} + +resource policySetAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: 'dep-<>-psa-${serviceShort}' + location: location + properties: { + displayName: 'Test case assignment' + policyDefinitionId: policySet.id + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyAssignmentId: policySetAssignment.id + displayName: '[Display Name] policy exempt (resource group scope)' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + assignmentScopeValidation: 'Default' + description: 'My description' + resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + kind: 'resourceLocation' + in: [ + 'westcentralus' + ] + } + ] + } + ] + policyDefinitionReferenceIds: [ + policySet.properties.policyDefinitions[0].policyDefinitionReferenceId + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.min/deploy.test.bicep new file mode 100644 index 000000000..fdb3496ec --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/rg.min/deploy.test.bicep @@ -0,0 +1,52 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.policyexemptions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apergmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: 'dep-<>-${serviceShort}-rgloc' + location: location + properties: { + displayName: '[Depedency] Audit resource location matches resource group location (management group scope)' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/0a914e76-4921-4c19-b460-a2d36003525a' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyAssignmentId: policyAssignment.id + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.common/deploy.test.bicep new file mode 100644 index 000000000..1e0643f6a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.common/deploy.test.bicep @@ -0,0 +1,111 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apesubcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2021-06-01' = { + name: 'dep-<>-polDef-AuditKvlt-${serviceShort}' + properties: { + policyRule: { + if: { + allOf: [ + { + equals: 'Microsoft.KeyVault/vaults' + field: 'type' + } + ] + } + then: { + effect: '[parameters(\'effect\')]' + } + } + parameters: { + effect: { + allowedValues: [ + 'Audit' + ] + defaultValue: 'Audit' + type: 'String' + } + } + } +} + +resource policySet 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: 'dep-<>-polSet-${serviceShort}' + properties: { + policyDefinitions: [ + { + parameters: { + effect: { + value: 'Audit' + } + } + policyDefinitionId: policyDefinition.id + policyDefinitionReferenceId: policyDefinition.name + } + ] + } +} + +resource policySetAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: 'dep-<>-psa-${serviceShort}' + location: location + properties: { + displayName: 'Test case assignment' + policyDefinitionId: policySet.id + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyAssignmentId: policySetAssignment.id + displayName: '[Display Name] policy exempt (subscription scope)' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + assignmentScopeValidation: 'Default' + description: 'My description' + resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + kind: 'resourceLocation' + in: [ + 'westcentralus' + ] + } + ] + } + ] + policyDefinitionReferenceIds: [ + policySet.properties.policyDefinitions[0].policyDefinitionReferenceId + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.min/deploy.test.bicep new file mode 100644 index 000000000..026e1e92e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/.test/sub.min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The location to deploy resources to.') +param location 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 = 'apesubmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource policyAssignment 'Microsoft.Authorization/policyAssignments@2021-06-01' = { + name: 'dep-<>-${serviceShort}-rgloc' + location: location + properties: { + displayName: '[Depedency] Audit resource location matches resource group location (management group scope)' + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/0a914e76-4921-4c19-b460-a2d36003525a' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyAssignmentId: policyAssignment.id + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/deploy.bicep new file mode 100644 index 000000000..910f0ab92 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/deploy.bicep @@ -0,0 +1,133 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for management group, subscription and resource group scopes.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy exemption. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. The group ID of the management group to be exempted from the policy assignment. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID of the subscription to be exempted from the policy assignment. Cannot use with management group ID parameter.') +param subscriptionId string = '' + +@sys.description('Optional. The name of the resource group to be exempted from the policy assignment. Must also use the subscription ID parameter.') +param resourceGroupName string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. The option whether validate the exemption is at or under the assignment scope.') +@allowed([ + '' + 'Default' + 'DoNotValidate' +]) +param assignmentScopeValidation string = '' + +@sys.description('Optional. The resource selector list to filter policies by resource properties.') +param resourceSelectors array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policyExemption_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyExemption-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + displayName: displayName + description: description + metadata: metadata + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: policyDefinitionReferenceIds + expiresOn: expiresOn + location: location + assignmentScopeValidation: assignmentScopeValidation + resourceSelectors: resourceSelectors + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyExemption_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-PolicyExemption-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + displayName: displayName + description: description + metadata: metadata + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: policyDefinitionReferenceIds + expiresOn: expiresOn + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policyExemption_rg 'resourceGroup/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicyExemption-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + name: name + displayName: displayName + description: description + metadata: metadata + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: policyDefinitionReferenceIds + expiresOn: expiresOn + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Exemption Name.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_sub.outputs.name : policyExemption_rg.outputs.name) + +@sys.description('Policy Exemption resource ID.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_sub.outputs.resourceId : policyExemption_rg.outputs.resourceId) + +@sys.description('Policy Exemption Scope.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? policyExemption_sub.outputs.scope : policyExemption_rg.outputs.scope) diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep new file mode 100644 index 000000000..e77971947 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/deploy.bicep @@ -0,0 +1,85 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for management group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy assignment. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. The option whether validate the exemption is at or under the assignment scope.') +@allowed([ + '' + 'Default' + 'DoNotValidate' +]) +param assignmentScopeValidation string = '' + +@sys.description('Optional. The resource selector list to filter policies by resource properties.') +param resourceSelectors array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyExemption 'Microsoft.Authorization/policyExemptions@2022-07-01-preview' = { + name: name + properties: { + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : null + assignmentScopeValidation: !empty(assignmentScopeValidation) ? assignmentScopeValidation : null + resourceSelectors: resourceSelectors + } +} + +@sys.description('Policy Exemption Name.') +output name string = policyExemption.name + +@sys.description('Policy Exemption resource ID.') +output resourceId string = policyExemption.id + +@sys.description('Policy Exemption Scope.') +output scope string = managementGroup().id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/readme.md new file mode 100644 index 000000000..abb0da4ac --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/readme.md @@ -0,0 +1,53 @@ +# Policy Exemptions on Management Group level `[Microsoft.Authorization/policyExemptions/managementGroup]` + +With this module you can create policy exemptions on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2022-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for management group scope. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `assignmentScopeValidation` | string | `''` | `['', Default, DoNotValidate]` | The option whether validate the exemption is at or under the assignment scope. | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy assignment. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `resourceSelectors` | array | `[]` | | The resource selector list to filter policies by resource properties. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/readme.md new file mode 100644 index 000000000..a3dc4ce0c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/readme.md @@ -0,0 +1,700 @@ +# Policy Exemptions `[Microsoft.Authorization/policyExemptions]` + +With this module you can create policy exemptions across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2022-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for management group, subscription and resource group scopes. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `assignmentScopeValidation` | string | `''` | `['', Default, DoNotValidate]` | The option whether validate the exemption is at or under the assignment scope. | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy exemption. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | The group ID of the management group to be exempted from the policy assignment. If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `resourceGroupName` | string | `''` | | The name of the resource group to be exempted from the policy assignment. Must also use the subscription ID parameter. | +| `resourceSelectors` | array | `[]` | | The resource selector list to filter policies by resource properties. | +| `subscriptionId` | string | `''` | | The subscription ID of the subscription to be exempted from the policy assignment. Cannot use with management group ID parameter. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +### Parameter Usage: `resourceSelectors` + +To deploy Resource Selectors, you can apply the following syntax + + +

+ +Parameter JSON format + +```json +"resourceSelectors": [ + { + "name": "TemporaryMitigation", + "selectors": [ + { + "kind": "resourceLocation", + "in": [ + "westcentralus" + ] + } + ] + } +] +``` + +
+ +
+ +Bicep format + +```bicep +resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + kind: 'resourceLocation' + in: [ + 'westcentralus' + ] + } + ] + } +] +``` + +
+

+ +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policyexemption 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policyexemptions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policyexemption 'yourpath/modules/Microsoft.Authorization.policyExemptions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Considerations + +- Policy Exemptions have a dependency on Policy Assignments being applied before creating an exemption. You can use the Policy Assignment [Module](../policyAssignments/deploy.bicep) to deploy a Policy Assignment and then create the exemption for it on the required scope. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg.Common

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apemgcom' + params: { + // Required parameters + name: '<>apemgcom001' + policyAssignmentId: '' + // Non-required parameters + assignmentScopeValidation: 'Default' + description: 'My description' + displayName: '[Display Name] policy exempt (management group scope)' + enableDefaultTelemetry: '' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + policyDefinitionReferenceIds: [ + '' + ] + resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + in: [ + 'westcentralus' + ] + kind: 'resourceLocation' + } + ] + } + ] + } +} +``` + +
+

+ +

+ +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": "<>apemgcom001" + }, + "policyAssignmentId": { + "value": "" + }, + // Non-required parameters + "assignmentScopeValidation": { + "value": "Default" + }, + "description": { + "value": "My description" + }, + "displayName": { + "value": "[Display Name] policy exempt (management group scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "expiresOn": { + "value": "2025-10-02T03:57:00Z" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "policyDefinitionReferenceIds": { + "value": [ + "" + ] + }, + "resourceSelectors": { + "value": [ + { + "name": "TemporaryMitigation", + "selectors": [ + { + "in": [ + "westcentralus" + ], + "kind": "resourceLocation" + } + ] + } + ] + } + } +} +``` + +
+

+ +

Example 2: Mg.Min

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apemgmin' + params: { + // Required parameters + name: '<>apemgmin001' + policyAssignmentId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>apemgmin001" + }, + "policyAssignmentId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 3: Rg.Common

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apergcom' + params: { + // Required parameters + name: '<>apergcom001' + policyAssignmentId: '' + // Non-required parameters + assignmentScopeValidation: 'Default' + description: 'My description' + displayName: '[Display Name] policy exempt (resource group scope)' + enableDefaultTelemetry: '' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + policyDefinitionReferenceIds: [ + '' + ] + resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + in: [ + 'westcentralus' + ] + kind: 'resourceLocation' + } + ] + } + ] + } +} +``` + +
+

+ +

+ +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": "<>apergcom001" + }, + "policyAssignmentId": { + "value": "" + }, + // Non-required parameters + "assignmentScopeValidation": { + "value": "Default" + }, + "description": { + "value": "My description" + }, + "displayName": { + "value": "[Display Name] policy exempt (resource group scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "expiresOn": { + "value": "2025-10-02T03:57:00Z" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "policyDefinitionReferenceIds": { + "value": [ + "" + ] + }, + "resourceSelectors": { + "value": [ + { + "name": "TemporaryMitigation", + "selectors": [ + { + "in": [ + "westcentralus" + ], + "kind": "resourceLocation" + } + ] + } + ] + } + } +} +``` + +
+

+ +

Example 4: Rg.Min

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apergmin' + params: { + // Required parameters + name: '<>apergmin001' + policyAssignmentId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>apergmin001" + }, + "policyAssignmentId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 5: Sub.Common

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apesubcom' + params: { + // Required parameters + name: '<>apesubcom001' + policyAssignmentId: '' + // Non-required parameters + assignmentScopeValidation: 'Default' + description: 'My description' + displayName: '[Display Name] policy exempt (subscription scope)' + enableDefaultTelemetry: '' + exemptionCategory: 'Waiver' + expiresOn: '2025-10-02T03:57:00Z' + metadata: { + category: 'Security' + } + policyDefinitionReferenceIds: [ + '' + ] + resourceSelectors: [ + { + name: 'TemporaryMitigation' + selectors: [ + { + in: [ + 'westcentralus' + ] + kind: 'resourceLocation' + } + ] + } + ] + } +} +``` + +
+

+ +

+ +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": "<>apesubcom001" + }, + "policyAssignmentId": { + "value": "" + }, + // Non-required parameters + "assignmentScopeValidation": { + "value": "Default" + }, + "description": { + "value": "My description" + }, + "displayName": { + "value": "[Display Name] policy exempt (subscription scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "exemptionCategory": { + "value": "Waiver" + }, + "expiresOn": { + "value": "2025-10-02T03:57:00Z" + }, + "metadata": { + "value": { + "category": "Security" + } + }, + "policyDefinitionReferenceIds": { + "value": [ + "" + ] + }, + "resourceSelectors": { + "value": [ + { + "name": "TemporaryMitigation", + "selectors": [ + { + "in": [ + "westcentralus" + ], + "kind": "resourceLocation" + } + ] + } + ] + } + } +} +``` + +
+

+ +

Example 6: Sub.Min

+ +
+ +via Bicep module + +```bicep +module policyExemptions './Microsoft.Authorization/policyExemptions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apesubmin' + params: { + // Required parameters + name: '<>apesubmin001' + policyAssignmentId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>apesubmin001" + }, + "policyAssignmentId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep new file mode 100644 index 000000000..89b676692 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/deploy.bicep @@ -0,0 +1,84 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for resource group scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy exemption. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. The option whether validate the exemption is at or under the assignment scope.') +@allowed([ + '' + 'Default' + 'DoNotValidate' +]) +param assignmentScopeValidation string = '' + +@sys.description('Optional. The resource selector list to filter policies by resource properties.') +param resourceSelectors array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyExemption 'Microsoft.Authorization/policyExemptions@2022-07-01-preview' = { + name: name + properties: { + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : null + assignmentScopeValidation: !empty(assignmentScopeValidation) ? assignmentScopeValidation : null + resourceSelectors: resourceSelectors + } +} + +@sys.description('Policy Exemption Name.') +output name string = policyExemption.name + +@sys.description('Policy Exemption resource ID.') +output resourceId string = policyExemption.id + +@sys.description('Policy Exemption Scope.') +output scope string = resourceGroup().id + +@sys.description('The name of the resource group the policy exemption was applied at.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md new file mode 100644 index 000000000..c4b90c71d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/readme.md @@ -0,0 +1,53 @@ +# Policy Exemptions on Resource Group level `[Microsoft.Authorization/policyExemptions/resourceGroup]` + +With this module you can create policy exemptions on a resource group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2022-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for resource group scope. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `assignmentScopeValidation` | string | `''` | `['', Default, DoNotValidate]` | The option whether validate the exemption is at or under the assignment scope. | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy exemption. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `resourceSelectors` | array | `[]` | | The resource selector list to filter policies by resource properties. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceGroupName` | string | The name of the resource group the policy exemption was applied at. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep new file mode 100644 index 000000000..33ec63856 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/deploy.bicep @@ -0,0 +1,85 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy exemption. Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the policy exemption. Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description of the policy exemption.') +param description string = '' + +@sys.description('Optional. The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Optional. The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated.') +@allowed([ + 'Mitigated' + 'Waiver' +]) +param exemptionCategory string = 'Mitigated' + +@sys.description('Required. The resource ID of the policy assignment that is being exempted.') +param policyAssignmentId string + +@sys.description('Optional. The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition.') +param policyDefinitionReferenceIds array = [] + +@sys.description('Optional. The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z.') +param expiresOn string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. The option whether validate the exemption is at or under the assignment scope.') +@allowed([ + '' + 'Default' + 'DoNotValidate' +]) +param assignmentScopeValidation string = '' + +@sys.description('Optional. The resource selector list to filter policies by resource properties.') +param resourceSelectors array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policyExemption 'Microsoft.Authorization/policyExemptions@2022-07-01-preview' = { + name: name + properties: { + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + exemptionCategory: exemptionCategory + policyAssignmentId: policyAssignmentId + policyDefinitionReferenceIds: !empty(policyDefinitionReferenceIds) ? policyDefinitionReferenceIds : [] + expiresOn: !empty(expiresOn) ? expiresOn : null + assignmentScopeValidation: !empty(assignmentScopeValidation) ? assignmentScopeValidation : null + resourceSelectors: resourceSelectors + } +} + +@sys.description('Policy Exemption Name.') +output name string = policyExemption.name + +@sys.description('Policy Exemption resource ID.') +output resourceId string = policyExemption.id + +@sys.description('Policy Exemption Scope.') +output scope string = subscription().id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/readme.md new file mode 100644 index 000000000..6f28134cd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/readme.md @@ -0,0 +1,53 @@ +# Policy Exemptions on Subscription level `[Microsoft.Authorization/policyExemptions/subscription]` + +With this module you can create policy exemptions on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policyExemptions` | [2022-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-07-01-preview/policyExemptions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy exemption. Maximum length is 64 characters for subscription scope. | +| `policyAssignmentId` | string | The resource ID of the policy assignment that is being exempted. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `assignmentScopeValidation` | string | `''` | `['', Default, DoNotValidate]` | The option whether validate the exemption is at or under the assignment scope. | +| `description` | string | `''` | | The description of the policy exemption. | +| `displayName` | string | `''` | | The display name of the policy exemption. Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `exemptionCategory` | string | `'Mitigated'` | `[Mitigated, Waiver]` | The policy exemption category. Possible values are Waiver and Mitigated. Default is Mitigated. | +| `expiresOn` | string | `''` | | The expiration date and time (in UTC ISO 8601 format yyyy-MM-ddTHH:mm:ssZ) of the policy exemption. e.g. 2021-10-02T03:57:00.000Z. | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `metadata` | object | `{object}` | | The policy exemption metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `policyDefinitionReferenceIds` | array | `[]` | | The policy definition reference ID list when the associated policy assignment is an assignment of a policy set definition. | +| `resourceSelectors` | array | `[]` | | The resource selector list to filter policies by resource properties. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Exemption Name. | +| `resourceId` | string | Policy Exemption resource ID. | +| `scope` | string | Policy Exemption Scope. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policyExemptions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.common/deploy.test.bicep new file mode 100644 index 000000000..0d1c7024d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.common/deploy.test.bicep @@ -0,0 +1,68 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'apsdmgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitions: [ + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + policyDefinitionReferenceId: 'Allowed locations_1' + } + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988' + policyDefinitionReferenceId: 'Allowed locations for resource groups_1' + } + ] + // Non-required parameters + description: '[Description] This policy set definition is deployed at management group scope' + displayName: '[DisplayName] This policy set definition is deployed at management group scope' + metadata: { + category: 'Security' + version: '1' + } + policyDefinitionGroups: [ + { + name: 'Network' + } + { + name: 'ARM' + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.min/deploy.test.bicep new file mode 100644 index 000000000..7b329e9be --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/mg.min/deploy.test.bicep @@ -0,0 +1,35 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'apsdmgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitions: [ + { + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.common/deploy.test.bicep new file mode 100644 index 000000000..ace1603ff --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.common/deploy.test.bicep @@ -0,0 +1,68 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'apsdsubcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitions: [ + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + policyDefinitionReferenceId: 'Allowed locations_1' + } + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988' + policyDefinitionReferenceId: 'Allowed locations for resource groups_1' + } + ] + // Non-required parameters + description: '[Description] This policy set definition is deployed at subscription scope' + displayName: '[DisplayName] This policy set definition is deployed at subscription scope' + metadata: { + category: 'Security' + version: '1' + } + policyDefinitionGroups: [ + { + name: 'Network' + } + { + name: 'ARM' + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.min/deploy.test.bicep new file mode 100644 index 000000000..93e5a1c6f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/.test/sub.min/deploy.test.bicep @@ -0,0 +1,35 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'apsdsubmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policyDefinitions: [ + { + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/deploy.bicep new file mode 100644 index 000000000..25b3784ec --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/deploy.bicep @@ -0,0 +1,89 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy Set Definition (Initiative).') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the Set Definition (Initiative). Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description name of the Set Definition (Initiative).') +param description string = '' + +@sys.description('Optional. The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID of the subscription (Scope). Cannot be used with managementGroupId.') +param subscriptionId string = '' + +@sys.description('Optional. The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Required. The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters.') +param policyDefinitions array + +@sys.description('Optional. The metadata describing groups of policy definition references within the Policy Set Definition (Initiative).') +param policyDefinitionGroups array = [] + +@sys.description('Optional. The Set Definition (Initiative) parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module policySetDefinition_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicySetDefinition-MG-Module' + scope: managementGroup(managementGroupId) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module policySetDefinition_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-PolicySetDefinition-Sub-Module' + scope: subscription(subscriptionId) + params: { + name: name + displayName: !empty(displayName) ? displayName : '' + description: !empty(description) ? description : '' + metadata: !empty(metadata) ? metadata : {} + parameters: !empty(parameters) ? parameters : {} + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('Policy Set Definition Name.') +output name string = empty(subscriptionId) ? policySetDefinition_mg.outputs.name : policySetDefinition_sub.outputs.name + +@sys.description('Policy Set Definition resource ID.') +output resourceId string = empty(subscriptionId) ? policySetDefinition_mg.outputs.resourceId : policySetDefinition_sub.outputs.resourceId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep new file mode 100644 index 000000000..b39367e43 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/deploy.bicep @@ -0,0 +1,62 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Specifies the name of the policy Set Definition (Initiative).') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the Set Definition (Initiative). Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description name of the Set Definition (Initiative).') +param description string = '' + +@sys.description('Optional. The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Required. The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters.') +param policyDefinitions array + +@sys.description('Optional. The metadata describing groups of policy definition references within the Policy Set Definition (Initiative).') +param policyDefinitionGroups array = [] + +@sys.description('Optional. The Set Definition (Initiative) parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policySetDefinition 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + } +} + +@sys.description('Policy Set Definition Name.') +output name string = policySetDefinition.name + +@sys.description('Policy Set Definition resource ID.') +output resourceId string = policySetDefinition.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md new file mode 100644 index 000000000..748041afd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/readme.md @@ -0,0 +1,49 @@ +# Policy Set Definitions on Management Group level `[Microsoft.Authorization/policySetDefinitions/managementGroup]` + +With this module you can create policy set definitions on a management group level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policySetDefinitions` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policySetDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy Set Definition (Initiative). | +| `policyDefinitions` | array | The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description name of the Set Definition (Initiative). | +| `displayName` | string | `''` | The display name of the Set Definition (Initiative). Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `metadata` | object | `{object}` | The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `parameters` | object | `{object}` | The Set Definition (Initiative) parameters that can be used in policy definition references. | +| `policyDefinitionGroups` | array | `[]` | The metadata describing groups of policy definition references within the Policy Set Definition (Initiative). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Set Definition Name. | +| `resourceId` | string | Policy Set Definition resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/readme.md new file mode 100644 index 000000000..0e94c3a4e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/readme.md @@ -0,0 +1,573 @@ +# Policy Set Definitions `[Microsoft.Authorization/policySetDefinitions]` + +With this module you can create policy set definitions across the management group or subscription scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policySetDefinitions` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policySetDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy Set Definition (Initiative). | +| `policyDefinitions` | array | The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description name of the Set Definition (Initiative). | +| `displayName` | string | `''` | The display name of the Set Definition (Initiative). Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group (Scope). If not provided, will use the current scope for deployment. | +| `metadata` | object | `{object}` | The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `parameters` | object | `{object}` | The Set Definition (Initiative) parameters that can be used in policy definition references. | +| `policyDefinitionGroups` | array | `[]` | The metadata describing groups of policy definition references within the Policy Set Definition (Initiative). | +| `subscriptionId` | string | `''` | The subscription ID of the subscription (Scope). Cannot be used with managementGroupId. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module policysetdefinition 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.policysetdefinitions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module policysetdefinition 'yourpath/modules/Microsoft.Authorization.policySetDefinitions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Set Definition Name. | +| `resourceId` | string | Policy Set Definition resource ID. | + +## Considerations + +- Policy Set Definitions (Initiatives) have a dependency on Policy Assignments being applied before creating an initiative. You can use the Policy Assignment [Module](../policyDefinitions/deploy.bicep) to deploy a Policy Definition and then create an initiative for it on the required scope. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg.Common

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apsdmgcom' + params: { + // Required parameters + name: '<>apsdmgcom001' + policyDefinitions: [ + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + policyDefinitionReferenceId: 'Allowed locations_1' + } + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988' + policyDefinitionReferenceId: 'Allowed locations for resource groups_1' + } + ] + // Non-required parameters + description: '[Description] This policy set definition is deployed at management group scope' + displayName: '[DisplayName] This policy set definition is deployed at management group scope' + enableDefaultTelemetry: '' + metadata: { + category: 'Security' + version: '1' + } + policyDefinitionGroups: [ + { + name: 'Network' + } + { + name: 'ARM' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>apsdmgcom001" + }, + "policyDefinitions": { + "value": [ + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c", + "policyDefinitionReferenceId": "Allowed locations_1" + }, + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988", + "policyDefinitionReferenceId": "Allowed locations for resource groups_1" + } + ] + }, + // Non-required parameters + "description": { + "value": "[Description] This policy set definition is deployed at management group scope" + }, + "displayName": { + "value": "[DisplayName] This policy set definition is deployed at management group scope" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1" + } + }, + "policyDefinitionGroups": { + "value": [ + { + "name": "Network" + }, + { + "name": "ARM" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Mg.Min

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apsdmgmin' + params: { + // Required parameters + name: '<>apsdmgmin001' + policyDefinitions: [ + { + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + } + ] + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>apsdmgmin001" + }, + "policyDefinitions": { + "value": [ + { + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" + } + ] + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 3: Sub.Common

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apsdsubcom' + params: { + // Required parameters + name: '<>apsdsubcom001' + policyDefinitions: [ + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + policyDefinitionReferenceId: 'Allowed locations_1' + } + { + groupNames: [ + 'ARM' + ] + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988' + policyDefinitionReferenceId: 'Allowed locations for resource groups_1' + } + ] + // Non-required parameters + description: '[Description] This policy set definition is deployed at subscription scope' + displayName: '[DisplayName] This policy set definition is deployed at subscription scope' + enableDefaultTelemetry: '' + metadata: { + category: 'Security' + version: '1' + } + policyDefinitionGroups: [ + { + name: 'Network' + } + { + name: 'ARM' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>apsdsubcom001" + }, + "policyDefinitions": { + "value": [ + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c", + "policyDefinitionReferenceId": "Allowed locations_1" + }, + { + "groupNames": [ + "ARM" + ], + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988", + "policyDefinitionReferenceId": "Allowed locations for resource groups_1" + } + ] + }, + // Non-required parameters + "description": { + "value": "[Description] This policy set definition is deployed at subscription scope" + }, + "displayName": { + "value": "[DisplayName] This policy set definition is deployed at subscription scope" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "metadata": { + "value": { + "category": "Security", + "version": "1" + } + }, + "policyDefinitionGroups": { + "value": [ + { + "name": "Network" + }, + { + "name": "ARM" + } + ] + } + } +} +``` + +
+

+ +

Example 4: Sub.Min

+ +
+ +via Bicep module + +```bicep +module policySetDefinitions './Microsoft.Authorization/policySetDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-apsdsubmin' + params: { + // Required parameters + name: '<>apsdsubmin001' + policyDefinitions: [ + { + parameters: { + listOfAllowedLocations: { + value: [ + 'australiaeast' + ] + } + } + policyDefinitionId: '/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c' + } + ] + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>apsdsubmin001" + }, + "policyDefinitions": { + "value": [ + { + "parameters": { + "listOfAllowedLocations": { + "value": [ + "australiaeast" + ] + } + }, + "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c" + } + ] + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep new file mode 100644 index 000000000..26f24a4ce --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/deploy.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +@sys.description('Required. Specifies the name of the policy Set Definition (Initiative). Maximum length is 64 characters for subscription scope.') +@maxLength(64) +param name string + +@sys.description('Optional. The display name of the Set Definition (Initiative). Maximum length is 128 characters.') +@maxLength(128) +param displayName string = '' + +@sys.description('Optional. The description name of the Set Definition (Initiative).') +param description string = '' + +@sys.description('Optional. The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs.') +param metadata object = {} + +@sys.description('Required. The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters.') +param policyDefinitions array + +@sys.description('Optional. The metadata describing groups of policy definition references within the Policy Set Definition (Initiative).') +param policyDefinitionGroups array = [] + +@sys.description('Optional. The Set Definition (Initiative) parameters that can be used in policy definition references.') +param parameters object = {} + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource policySetDefinition 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = { + name: name + properties: { + policyType: 'Custom' + displayName: !empty(displayName) ? displayName : null + description: !empty(description) ? description : null + metadata: !empty(metadata) ? metadata : null + parameters: !empty(parameters) ? parameters : null + policyDefinitions: policyDefinitions + policyDefinitionGroups: !empty(policyDefinitionGroups) ? policyDefinitionGroups : [] + } +} + +@sys.description('Policy Set Definition Name.') +output name string = policySetDefinition.name + +@sys.description('Policy Set Definition resource ID.') +output resourceId string = policySetDefinition.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/readme.md new file mode 100644 index 000000000..ddc30ce9a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/readme.md @@ -0,0 +1,49 @@ +# Policy Set Definitions on Subscription level `[Microsoft.Authorization/policySetDefinitions/subscription]` + +With this module you can create policy set definitions on a subscription level. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/policySetDefinitions` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2021-06-01/policySetDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Specifies the name of the policy Set Definition (Initiative). Maximum length is 64 characters for subscription scope. | +| `policyDefinitions` | array | The array of Policy definitions object to include for this policy set. Each object must include the Policy definition ID, and optionally other properties like parameters. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description name of the Set Definition (Initiative). | +| `displayName` | string | `''` | The display name of the Set Definition (Initiative). Maximum length is 128 characters. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `metadata` | object | `{object}` | The Set Definition (Initiative) metadata. Metadata is an open ended object and is typically a collection of key-value pairs. | +| `parameters` | object | `{object}` | The Set Definition (Initiative) parameters that can be used in policy definition references. | +| `policyDefinitionGroups` | array | `[]` | The metadata describing groups of policy definition references within the Policy Set Definition (Initiative). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Policy Set Definition Name. | +| `resourceId` | string | Policy Set Definition resource ID. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/policySetDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/dependencies.bicep new file mode 100644 index 000000000..d36777043 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/deploy.test.bicep new file mode 100644 index 000000000..da7300e44 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/deploy.test.bicep @@ -0,0 +1,50 @@ +targetScope = 'managementGroup' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'aramgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('<>') + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-<>-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Backup Reader' + description: 'Role Assignment (management group scope)' + managementGroupId: last(split(managementGroup().id, '/')) + principalType: 'ServicePrincipal' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/interim.dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/interim.dependencies.bicep new file mode 100644 index 000000000..b6b3cef62 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.common/interim.dependencies.bicep @@ -0,0 +1,27 @@ +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 + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/dependencies.bicep new file mode 100644 index 000000000..d36777043 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/deploy.test.bicep new file mode 100644 index 000000000..3380c1022 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/deploy.test.bicep @@ -0,0 +1,48 @@ +targetScope = 'managementGroup' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'interim.dependencies.bicep' = { + scope: subscription('<>') + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + managedIdentityName: 'dep-<>-msi-${serviceShort}' + resourceGroupName: resourceGroupName + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Storage Queue Data Reader' + principalType: 'ServicePrincipal' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/interim.dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/interim.dependencies.bicep new file mode 100644 index 000000000..b6b3cef62 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/mg.min/interim.dependencies.bicep @@ -0,0 +1,27 @@ +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 + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = nestedDependencies.outputs.managedIdentityPrincipalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/dependencies.bicep new file mode 100644 index 000000000..5681a8998 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/deploy.test.bicep new file mode 100644 index 000000000..1492325b6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.common/deploy.test.bicep @@ -0,0 +1,55 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'arargcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Backup Reader' + description: 'Role Assignment (resource group scope)' + principalType: 'ServicePrincipal' + resourceGroupName: resourceGroup.name + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/dependencies.bicep new file mode 100644 index 000000000..5681a8998 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/deploy.test.bicep new file mode 100644 index 000000000..d0f3d5f94 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/rg.min/deploy.test.bicep @@ -0,0 +1,54 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Storage Queue Data Reader' + principalType: 'ServicePrincipal' + resourceGroupName: resourceGroup.name + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/dependencies.bicep new file mode 100644 index 000000000..5681a8998 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/deploy.test.bicep new file mode 100644 index 000000000..c8b6e9aee --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.common/deploy.test.bicep @@ -0,0 +1,53 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'arasubcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Backup Reader' + description: 'Role Assignment (subscription scope)' + principalType: 'ServicePrincipal' + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/dependencies.bicep new file mode 100644 index 000000000..5681a8998 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/deploy.test.bicep new file mode 100644 index 000000000..ca0f81bab --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/.test/sub.min/deploy.test.bicep @@ -0,0 +1,52 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roleassignments-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + roleDefinitionIdOrName: 'Storage Queue Data Reader' + principalType: 'ServicePrincipal' + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/deploy.bicep new file mode 100644 index 000000000..5fcf0e8fd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/deploy.bicep @@ -0,0 +1,123 @@ +targetScope = 'managementGroup' + +@sys.description('Required. You can provide either the display name of the role definition, 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 telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module roleAssignment_mg 'managementGroup/deploy.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 : '' + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleAssignment_sub 'subscription/deploy.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 : '' + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleAssignment_rg 'resourceGroup/deploy.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 : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep new file mode 100644 index 000000000..8915cffb7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/deploy.bicep @@ -0,0 +1,479 @@ +targetScope = 'managementGroup' + +@sys.description('Required. You can provide either the display name of the role definition, 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 = '' + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +var builtInRoleNames = { + 'Access Review Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475' + AcrDelete: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + AcrImageSigner: '/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f' + AcrPull: '/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d' + AcrPush: '/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec' + AcrQuarantineReader: '/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04' + AcrQuarantineWriter: '/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608' + 'AgFood Platform Sensor Partner Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67' + 'AgFood Platform Service Admin': '/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3' + 'AgFood Platform Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728' + 'AgFood Platform Service Reader': '/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba' + 'AnyBuild Builder': '/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8' + 'API Management Developer Portal Content Editor': '/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729' + 'API Management Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c' + 'API Management Service Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61' + 'API Management Service Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d' + 'App Configuration Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b' + 'App Configuration Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071' + 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' + 'Application Insights Component Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e' + 'Application Insights Snapshot Debugger': '/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b' + 'Attestation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e' + 'Attestation Reader': '/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3' + 'Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867' + 'Automation Job Operator': '/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f' + 'Automation Operator': '/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404' + 'Automation Runbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5' + 'Autonomous Development Platform Data Contributor (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795' + 'Autonomous Development Platform Data Owner (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d' + 'Autonomous Development Platform Data Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093' + 'Avere Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a' + 'Avere Operator': '/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9' + 'Azure Arc Enabled Kubernetes Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd' + 'Azure Arc Kubernetes Admin': '/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96' + 'Azure Arc Kubernetes Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2' + 'Azure Arc Kubernetes Viewer': '/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4' + 'Azure Arc Kubernetes Writer': '/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1' + 'Azure Arc ScVmm Administrator role': '/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87' + 'Azure Arc ScVmm Private Cloud User': '/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda' + 'Azure Arc ScVmm Private Clouds Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9' + 'Azure Arc ScVmm VM Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b' + 'Azure Arc VMware Administrator role ': '/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f' + 'Azure Arc VMware Private Cloud User': '/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83' + 'Azure Arc VMware Private Clouds Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa' + 'Azure Arc VMware VM Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb' + 'Azure Center for SAP solutions administrator': '/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7' + 'Azure Center for SAP solutions Management role': '/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310' + 'Azure Center for SAP solutions reader': '/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b' + 'Azure Center for SAP solutions service role': '/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138' + 'Azure Center for SAP solutions Service role for management': '/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb' + 'Azure Connected Machine Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7' + 'Azure Connected Machine Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302' + 'Azure Connected Machine Resource Manager': '/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c' + 'Azure Connected SQL Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508' + 'Azure Digital Twins Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe' + 'Azure Digital Twins Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3' + 'Azure Event Hubs Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec' + 'Azure Event Hubs Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde' + 'Azure Event Hubs Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975' + 'Azure Extension for SQL Server Deployment': '/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d' + 'Azure Front Door Domain Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8' + 'Azure Front Door Domain Reader': '/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f' + 'Azure Front Door Secret Contributor': '/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5' + 'Azure Front Door Secret Reader': '/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca' + 'Azure Kubernetes Fleet Manager Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf' + 'Azure Kubernetes Fleet Manager RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba' + 'Azure Kubernetes Fleet Manager RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69' + 'Azure Kubernetes Fleet Manager RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80' + 'Azure Kubernetes Fleet Manager RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683' + 'Azure Kubernetes Service Cluster Admin Role': '/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8' + 'Azure Kubernetes Service Cluster Monitoring User': '/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6' + 'Azure Kubernetes Service Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f' + 'Azure Kubernetes Service Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8' + 'Azure Kubernetes Service Policy Add-on Deployment': '/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064' + 'Azure Kubernetes Service RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7' + 'Azure Kubernetes Service RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b' + 'Azure Kubernetes Service RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db' + 'Azure Kubernetes Service RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb' + 'Azure Maps Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb' + 'Azure Maps Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204' + 'Azure Maps Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa' + 'Azure Maps Search and Render Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005' + 'Azure Relay Listener': '/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d' + 'Azure Relay Owner': '/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38' + 'Azure Relay Sender': '/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d' + 'Azure Service Bus Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419' + 'Azure Service Bus Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' + 'Azure Service Bus Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' + 'Azure Spring Apps Connect Role': '/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b' + 'Azure Spring Apps Remote Debugging Role': '/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054' + 'Azure Spring Cloud Config Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b' + 'Azure Spring Cloud Config Server Reader': '/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7' + 'Azure Spring Cloud Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c' + 'Azure Spring Cloud Service Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1' + 'Azure Spring Cloud Service Registry Reader': '/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65' + 'Azure Stack HCI registration role': '/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06' + 'Azure Stack Registration Owner': '/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a' + 'Azure Traffic Controller Configuration Manager': '/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1' + 'Azure Usage Billing Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6' + 'Azure VM Managed identities restore Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd' + 'AzureML Compute Operator': '/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815' + 'AzureML Data Scientist': '/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121' + 'AzureML Metrics Writer (preview)': '/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae' + 'AzureML Registry User': '/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615' + 'Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b' + 'Backup Operator': '/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324' + 'Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912' + 'Bayer Ag Powered Services CWUM Solution User Role': '/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa' + 'Bayer Ag Powered Services GDU Solution': '/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042' + 'Bayer Ag Powered Services Imagery Solution': '/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588' + 'Billing Reader': '/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64' + 'BizTalk Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342' + 'Blockchain Member Node Access (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24' + 'Blueprint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4' + 'Blueprint Operator': '/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090' + 'CDN Endpoint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45' + 'CDN Endpoint Reader': '/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd' + 'CDN Profile Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432' + 'CDN Profile Reader': '/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af' + 'Chamber Admin': '/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a' + 'Chamber User': '/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32' + 'Classic Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f' + 'Classic Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25' + 'Classic Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d' + 'Classic Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb' + 'ClearDB MySQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe' + 'Code Signing Certificate Profile Signer': '/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958' + 'Code Signing Identity Verifier': '/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08' + 'Cognitive Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + 'Cognitive Services Custom Vision Contributor': '/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3' + 'Cognitive Services Custom Vision Deployment': '/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f' + 'Cognitive Services Custom Vision Labeler': '/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c' + 'Cognitive Services Custom Vision Reader': '/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73' + 'Cognitive Services Custom Vision Trainer': '/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b' + 'Cognitive Services Data Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c' + 'Cognitive Services Face Recognizer': '/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7' + 'Cognitive Services Immersive Reader User': '/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d' + 'Cognitive Services Language Owner': '/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498' + 'Cognitive Services Language Reader': '/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e' + 'Cognitive Services Language Writer': '/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8' + 'Cognitive Services LUIS Owner': '/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8' + 'Cognitive Services LUIS Reader': '/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226' + 'Cognitive Services LUIS Writer': '/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27' + 'Cognitive Services Metrics Advisor Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a' + 'Cognitive Services Metrics Advisor User': '/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8' + 'Cognitive Services OpenAI Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442' + 'Cognitive Services OpenAI User': '/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + 'Cognitive Services QnA Maker Editor': '/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025' + 'Cognitive Services QnA Maker Reader': '/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126' + 'Cognitive Services Speech Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181' + 'Cognitive Services Speech User': '/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447' + 'Cognitive Services User': '/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908' + 'Collaborative Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352' + 'Collaborative Runtime Operator': '/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102' + 'Compute Gallery Sharing Admin': '/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b' + 'ContainerApp Reader': '/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b' + Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + 'Cosmos DB Account Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8' + 'Cosmos DB Operator': '/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa' + CosmosBackupOperator: '/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb' + CosmosRestoreOperator: '/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f' + 'Cost Management Contributor': '/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430' + 'Cost Management Reader': '/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3' + 'Data Box Contributor': '/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5' + 'Data Box Reader': '/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027' + 'Data Factory Contributor': '/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5' + 'Data Labeling - Labeler': '/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab' + 'Data Lake Analytics Developer': '/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88' + 'Data Operator for Managed Disks': '/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e' + 'Data Purger': '/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90' + 'Deployment Environments User': '/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c' + 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' + 'Desktop Virtualization Application Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55' + 'Desktop Virtualization Contributor': '/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387' + 'Desktop Virtualization Host Pool Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc' + 'Desktop Virtualization Host Pool Reader': '/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822' + 'Desktop Virtualization Power On Contributor': '/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33' + 'Desktop Virtualization Power On Off Contributor': '/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e' + 'Desktop Virtualization Reader': '/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868' + 'Desktop Virtualization Session Host Operator': '/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408' + 'Desktop Virtualization User': '/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' + 'Desktop Virtualization User Session Operator': '/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6' + 'Desktop Virtualization Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c' + 'Desktop Virtualization Workspace Contributor': '/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b' + 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' + 'DevCenter Dev Box User': '/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05' + 'DevCenter Project Admin': '/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0' + 'Device Provisioning Service Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633' + 'Device Provisioning Service Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8' + 'Device Update Administrator': '/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a' + 'Device Update Content Administrator': '/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98' + 'Device Update Content Reader': '/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b' + 'Device Update Deployments Administrator': '/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432' + 'Device Update Deployments Reader': '/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f' + 'Device Update Reader': '/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f' + 'DevTest Labs User': '/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64' + 'DICOM Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8' + 'DICOM Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a' + 'Disk Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24' + 'Disk Pool Operator': '/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840' + 'Disk Restore Operator': '/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13' + 'Disk Snapshot Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce' + 'DNS Resolver Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d' + 'DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314' + 'DocumentDB Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' + 'Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2' + 'Domain Services Reader': '/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb' + 'Elastic SAN Owner': '/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406' + 'Elastic SAN Reader': '/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca' + 'Elastic SAN Volume Group Owner': '/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23' + 'EventGrid Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de' + 'EventGrid Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7' + 'EventGrid EventSubscription Contributor': '/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443' + 'EventGrid EventSubscription Reader': '/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405' + 'Experimentation Administrator': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c' + 'Experimentation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c' + 'Experimentation Metric Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0' + 'Experimentation Reader': '/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1' + 'FHIR Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd' + 'FHIR Data Converter': '/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24' + 'FHIR Data Exporter': '/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843' + 'FHIR Data Importer': '/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b' + 'FHIR Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508' + 'FHIR Data Writer': '/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913' + 'FHIR SMART User': '/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0' + 'Grafana Admin': '/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41' + 'Grafana Editor': '/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f' + 'Grafana Viewer': '/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769' + 'Graph Owner': '/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9' + 'Guest Configuration Resource Contributor': '/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31' + 'HDInsight Cluster Operator': '/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a' + 'HDInsight Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c' + 'Hierarchy Settings Administrator': '/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d' + 'Hybrid Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb' + 'Hybrid Server Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624' + 'Impact Reader': '/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e' + 'Impact Reporter': '/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a' + 'Integration Service Environment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8' + 'Integration Service Environment Developer': '/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec' + 'Intelligent Systems Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e' + 'IoT Hub Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f' + 'IoT Hub Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3' + 'IoT Hub Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47' + 'IoT Hub Twin Contributor': '/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c' + 'Key Vault Administrator': '/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483' + 'Key Vault Certificates Officer': '/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985' + 'Key Vault Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395' + 'Key Vault Crypto Officer': '/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603' + 'Key Vault Crypto Service Encryption User': '/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6' + 'Key Vault Crypto User': '/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424' + 'Key Vault Reader': '/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2' + 'Key Vault Secrets Officer': '/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7' + 'Key Vault Secrets User': '/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6' + 'Knowledge Consumer': '/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c' + 'Kubernetes Agentless Operator': '/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6' + 'Kubernetes Cluster - Azure Arc Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41' + 'Kubernetes Extension Contributor': '/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717' + 'Kubernetes Namespace User': '/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a' + 'Lab Assistant': '/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1' + 'Lab Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270' + 'Lab Creator': '/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead' + 'Lab Operator': '/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d' + 'Lab Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f' + 'Lab Services Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc' + 'Load Test Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e' + 'Load Test Owner': '/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6' + 'Load Test Reader': '/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081' + 'LocalNGFirewallAdministrator role': '/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2' + 'LocalRulestacksAdministrator role': '/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20' + 'Log Analytics Contributor': '/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293' + 'Log Analytics Reader': '/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893' + 'Logic App Contributor': '/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e' + 'Logic App Operator': '/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe' + 'Managed Application Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e' + 'Managed Application Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae' + 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' + 'Managed HSM contributor': '/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d' + 'Managed Identity Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59' + 'Managed Identity Operator': '/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830' + 'Managed Services Registration assignment Delete Role': '/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46' + 'Management Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c' + 'Management Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d' + 'Media Services Account Administrator': '/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466' + 'Media Services Live Events Administrator': '/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77' + 'Media Services Media Operator': '/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c' + 'Media Services Policy Administrator': '/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae' + 'Media Services Streaming Endpoints Administrator': '/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804' + 'Microsoft Sentinel Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a' + 'Microsoft Sentinel Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade' + 'Microsoft Sentinel Playbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5' + 'Microsoft Sentinel Reader': '/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb' + 'Microsoft Sentinel Responder': '/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056' + 'Microsoft.Kubernetes connected cluster role': '/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f' + 'Monitoring Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa' + 'Monitoring Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136' + 'Monitoring Metrics Publisher': '/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb' + 'Monitoring Reader': '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05' + 'MySQL Backup And Export Operator': '/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0' + 'Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + 'New Relic APM Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237' + 'Object Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b' + 'Object Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9' + 'Object Understanding Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745' + 'Object Understanding Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6' + Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + 'PlayFab Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726' + 'PlayFab Reader': '/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf' + 'Policy Insights Data Writer (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84' + 'Private DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f' + 'Project Babylon Data Curator': '/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889' + 'Project Babylon Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446' + 'Project Babylon Data Source Administrator': '/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f' + 'Purview role 1 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347' + 'Purview role 2 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803' + 'Purview role 3 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db' + 'Quota Request Operator': '/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125' + Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' + 'Reader and Data Access': '/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349' + 'Redis Cache Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17' + 'Remote Rendering Administrator': '/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e' + 'Remote Rendering Client': '/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a' + 'Reservation Purchaser': '/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689' + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Scheduled Patching Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6' + 'Scheduler Job Collections Contributor': '/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94' + 'Schema Registry Contributor (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25' + 'Schema Registry Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2' + 'Search Index Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7' + 'Search Index Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f' + 'Search Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + 'Security Admin': '/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd' + 'Security Assessment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5' + 'Security Detonation Chamber Publisher': '/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500' + 'Security Detonation Chamber Reader': '/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5' + 'Security Detonation Chamber Submission Manager': '/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce' + 'Security Detonation Chamber Submitter': '/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0' + 'Security Manager (Legacy)': '/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10' + 'Security Reader': '/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4' + 'Services Hub Operator': '/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b' + 'SignalR AccessKey Reader': '/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e' + 'SignalR App Server': '/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7' + 'SignalR REST API Owner': '/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521' + 'SignalR REST API Reader': '/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035' + 'SignalR Service Owner': '/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3' + 'SignalR/Web PubSub Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761' + 'Site Recovery Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567' + 'Site Recovery Operator': '/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca' + 'Site Recovery Reader': '/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149' + 'Spatial Anchors Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827' + 'Spatial Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c' + 'Spatial Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413' + 'SQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec' + 'SQL Managed Instance Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d' + 'SQL Security Manager': '/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3' + 'SQL Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437' + 'SqlDb Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57' + 'SqlMI Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872' + 'SqlVM Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d' + 'Storage Account Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1' + 'Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab' + 'Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12' + 'Storage Blob Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' + 'Storage Blob Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + 'Storage Blob Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + 'Storage Blob Delegator': '/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a' + 'Storage File Data SMB Share Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb' + 'Storage File Data SMB Share Elevated Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7' + 'Storage File Data SMB Share Reader': '/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314' + 'Storage Queue Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88' + 'Storage Queue Data Message Processor': '/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed' + 'Storage Queue Data Message Sender': '/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' + 'Storage Queue Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925' + 'Storage Table Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' + 'Storage Table Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6' + 'Stream Analytics Query Tester': '/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf' + 'Support Request Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e' + 'Tag Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + 'Template Spec Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b' + 'Template Spec Reader': '/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e' + 'Test Base Reader': '/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85' + 'Traffic Manager Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7' + 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + 'Video Indexer Restricted Viewer': '/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb' + 'Virtual Machine Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4' + 'Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + 'Virtual Machine Local User Login': '/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525' + 'Virtual Machine User Login': '/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52' + 'VM Scanner Operator': '/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd' + 'Web Plan Contributor': '/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b' + 'Web PubSub Service Owner (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4' + 'Web PubSub Service Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf' + 'Website Contributor': '/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772' + 'Windows Admin Center Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f' + 'Workbook Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad' + 'Workbook Reader': '/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d' + 'WorkloadBuilder Migration Agent Role': '/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c' +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/readme.md new file mode 100644 index 000000000..f3a1d453b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/readme.md @@ -0,0 +1,51 @@ +# Role Assignment on Management Group level `[Microsoft.Authorization/roleAssignments/managementGroup]` + +With this module you can perform role assignments on a management group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | You can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | + + +## Outputs + +| Output Name | 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_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/readme.md new file mode 100644 index 000000000..d90cd8c68 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/readme.md @@ -0,0 +1,538 @@ +# Role Assignments `[Microsoft.Authorization/roleAssignments]` + +This module deploys Role Assignments across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | You can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | | Group ID of the Management Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | +| `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` | 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 Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +resourceGroupName: 'target-resourceGroup' +``` + +
+

+ +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module roleassignment 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.roleassignments.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module roleassignment 'yourpath/modules/Microsoft.Authorization.roleAssignments/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | 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. | + +## Considerations + +This module can be deployed at the management group, subscription or resource group level + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg.Common

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-aramgcom' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Backup Reader' + // Non-required parameters + description: 'Role Assignment (management group scope)' + enableDefaultTelemetry: '' + 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": "Backup Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (management group scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "managementGroupId": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +

Example 2: Mg.Min

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-aramgmin' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Storage Queue Data Reader' + // Non-required parameters + enableDefaultTelemetry: '' + 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": "Storage Queue Data Reader" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + } + } +} +``` + +
+

+ +

Example 3: Rg.Common

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-arargcom' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Backup Reader' + // Non-required parameters + description: 'Role Assignment (resource group scope)' + enableDefaultTelemetry: '' + 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": "Backup Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (resource group scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 4: Rg.Min

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-arargmin' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Storage Queue Data Reader' + // Non-required parameters + enableDefaultTelemetry: '' + 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": "Storage Queue Data Reader" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "resourceGroupName": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 5: Sub.Common

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-arasubcom' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Backup Reader' + // Non-required parameters + description: 'Role Assignment (subscription scope)' + enableDefaultTelemetry: '' + 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": "Backup Reader" + }, + // Non-required parameters + "description": { + "value": "Role Assignment (subscription scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 6: Sub.Min

+ +
+ +via Bicep module + +```bicep +module roleAssignments './Microsoft.Authorization/roleAssignments/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-arasubmin' + params: { + // Required parameters + principalId: '' + roleDefinitionIdOrName: 'Storage Queue Data Reader' + // Non-required parameters + enableDefaultTelemetry: '' + 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": "Storage Queue Data Reader" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep new file mode 100644 index 000000000..2b1ff9d21 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep @@ -0,0 +1,481 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. You can provide either the display name of the role definition, 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 = '' + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var builtInRoleNames = { + 'Access Review Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475' + AcrDelete: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + AcrImageSigner: '/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f' + AcrPull: '/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d' + AcrPush: '/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec' + AcrQuarantineReader: '/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04' + AcrQuarantineWriter: '/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608' + 'AgFood Platform Sensor Partner Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67' + 'AgFood Platform Service Admin': '/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3' + 'AgFood Platform Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728' + 'AgFood Platform Service Reader': '/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba' + 'AnyBuild Builder': '/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8' + 'API Management Developer Portal Content Editor': '/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729' + 'API Management Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c' + 'API Management Service Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61' + 'API Management Service Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d' + 'App Configuration Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b' + 'App Configuration Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071' + 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' + 'Application Insights Component Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e' + 'Application Insights Snapshot Debugger': '/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b' + 'Attestation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e' + 'Attestation Reader': '/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3' + 'Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867' + 'Automation Job Operator': '/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f' + 'Automation Operator': '/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404' + 'Automation Runbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5' + 'Autonomous Development Platform Data Contributor (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795' + 'Autonomous Development Platform Data Owner (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d' + 'Autonomous Development Platform Data Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093' + 'Avere Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a' + 'Avere Operator': '/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9' + 'Azure Arc Enabled Kubernetes Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd' + 'Azure Arc Kubernetes Admin': '/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96' + 'Azure Arc Kubernetes Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2' + 'Azure Arc Kubernetes Viewer': '/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4' + 'Azure Arc Kubernetes Writer': '/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1' + 'Azure Arc ScVmm Administrator role': '/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87' + 'Azure Arc ScVmm Private Cloud User': '/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda' + 'Azure Arc ScVmm Private Clouds Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9' + 'Azure Arc ScVmm VM Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b' + 'Azure Arc VMware Administrator role ': '/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f' + 'Azure Arc VMware Private Cloud User': '/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83' + 'Azure Arc VMware Private Clouds Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa' + 'Azure Arc VMware VM Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb' + 'Azure Center for SAP solutions administrator': '/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7' + 'Azure Center for SAP solutions Management role': '/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310' + 'Azure Center for SAP solutions reader': '/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b' + 'Azure Center for SAP solutions service role': '/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138' + 'Azure Center for SAP solutions Service role for management': '/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb' + 'Azure Connected Machine Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7' + 'Azure Connected Machine Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302' + 'Azure Connected Machine Resource Manager': '/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c' + 'Azure Connected SQL Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508' + 'Azure Digital Twins Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe' + 'Azure Digital Twins Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3' + 'Azure Event Hubs Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec' + 'Azure Event Hubs Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde' + 'Azure Event Hubs Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975' + 'Azure Extension for SQL Server Deployment': '/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d' + 'Azure Front Door Domain Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8' + 'Azure Front Door Domain Reader': '/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f' + 'Azure Front Door Secret Contributor': '/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5' + 'Azure Front Door Secret Reader': '/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca' + 'Azure Kubernetes Fleet Manager Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf' + 'Azure Kubernetes Fleet Manager RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba' + 'Azure Kubernetes Fleet Manager RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69' + 'Azure Kubernetes Fleet Manager RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80' + 'Azure Kubernetes Fleet Manager RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683' + 'Azure Kubernetes Service Cluster Admin Role': '/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8' + 'Azure Kubernetes Service Cluster Monitoring User': '/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6' + 'Azure Kubernetes Service Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f' + 'Azure Kubernetes Service Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8' + 'Azure Kubernetes Service Policy Add-on Deployment': '/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064' + 'Azure Kubernetes Service RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7' + 'Azure Kubernetes Service RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b' + 'Azure Kubernetes Service RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db' + 'Azure Kubernetes Service RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb' + 'Azure Maps Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb' + 'Azure Maps Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204' + 'Azure Maps Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa' + 'Azure Maps Search and Render Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005' + 'Azure Relay Listener': '/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d' + 'Azure Relay Owner': '/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38' + 'Azure Relay Sender': '/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d' + 'Azure Service Bus Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419' + 'Azure Service Bus Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' + 'Azure Service Bus Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' + 'Azure Spring Apps Connect Role': '/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b' + 'Azure Spring Apps Remote Debugging Role': '/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054' + 'Azure Spring Cloud Config Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b' + 'Azure Spring Cloud Config Server Reader': '/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7' + 'Azure Spring Cloud Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c' + 'Azure Spring Cloud Service Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1' + 'Azure Spring Cloud Service Registry Reader': '/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65' + 'Azure Stack HCI registration role': '/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06' + 'Azure Stack Registration Owner': '/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a' + 'Azure Traffic Controller Configuration Manager': '/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1' + 'Azure Usage Billing Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6' + 'Azure VM Managed identities restore Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd' + 'AzureML Compute Operator': '/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815' + 'AzureML Data Scientist': '/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121' + 'AzureML Metrics Writer (preview)': '/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae' + 'AzureML Registry User': '/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615' + 'Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b' + 'Backup Operator': '/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324' + 'Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912' + 'Bayer Ag Powered Services CWUM Solution User Role': '/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa' + 'Bayer Ag Powered Services GDU Solution': '/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042' + 'Bayer Ag Powered Services Imagery Solution': '/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588' + 'Billing Reader': '/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64' + 'BizTalk Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342' + 'Blockchain Member Node Access (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24' + 'Blueprint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4' + 'Blueprint Operator': '/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090' + 'CDN Endpoint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45' + 'CDN Endpoint Reader': '/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd' + 'CDN Profile Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432' + 'CDN Profile Reader': '/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af' + 'Chamber Admin': '/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a' + 'Chamber User': '/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32' + 'Classic Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f' + 'Classic Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25' + 'Classic Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d' + 'Classic Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb' + 'ClearDB MySQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe' + 'Code Signing Certificate Profile Signer': '/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958' + 'Code Signing Identity Verifier': '/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08' + 'Cognitive Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + 'Cognitive Services Custom Vision Contributor': '/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3' + 'Cognitive Services Custom Vision Deployment': '/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f' + 'Cognitive Services Custom Vision Labeler': '/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c' + 'Cognitive Services Custom Vision Reader': '/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73' + 'Cognitive Services Custom Vision Trainer': '/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b' + 'Cognitive Services Data Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c' + 'Cognitive Services Face Recognizer': '/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7' + 'Cognitive Services Immersive Reader User': '/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d' + 'Cognitive Services Language Owner': '/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498' + 'Cognitive Services Language Reader': '/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e' + 'Cognitive Services Language Writer': '/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8' + 'Cognitive Services LUIS Owner': '/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8' + 'Cognitive Services LUIS Reader': '/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226' + 'Cognitive Services LUIS Writer': '/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27' + 'Cognitive Services Metrics Advisor Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a' + 'Cognitive Services Metrics Advisor User': '/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8' + 'Cognitive Services OpenAI Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442' + 'Cognitive Services OpenAI User': '/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + 'Cognitive Services QnA Maker Editor': '/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025' + 'Cognitive Services QnA Maker Reader': '/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126' + 'Cognitive Services Speech Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181' + 'Cognitive Services Speech User': '/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447' + 'Cognitive Services User': '/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908' + 'Collaborative Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352' + 'Collaborative Runtime Operator': '/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102' + 'Compute Gallery Sharing Admin': '/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b' + 'ContainerApp Reader': '/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b' + Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + 'Cosmos DB Account Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8' + 'Cosmos DB Operator': '/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa' + CosmosBackupOperator: '/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb' + CosmosRestoreOperator: '/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f' + 'Cost Management Contributor': '/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430' + 'Cost Management Reader': '/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3' + 'Data Box Contributor': '/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5' + 'Data Box Reader': '/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027' + 'Data Factory Contributor': '/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5' + 'Data Labeling - Labeler': '/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab' + 'Data Lake Analytics Developer': '/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88' + 'Data Operator for Managed Disks': '/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e' + 'Data Purger': '/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90' + 'Deployment Environments User': '/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c' + 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' + 'Desktop Virtualization Application Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55' + 'Desktop Virtualization Contributor': '/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387' + 'Desktop Virtualization Host Pool Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc' + 'Desktop Virtualization Host Pool Reader': '/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822' + 'Desktop Virtualization Power On Contributor': '/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33' + 'Desktop Virtualization Power On Off Contributor': '/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e' + 'Desktop Virtualization Reader': '/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868' + 'Desktop Virtualization Session Host Operator': '/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408' + 'Desktop Virtualization User': '/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' + 'Desktop Virtualization User Session Operator': '/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6' + 'Desktop Virtualization Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c' + 'Desktop Virtualization Workspace Contributor': '/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b' + 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' + 'DevCenter Dev Box User': '/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05' + 'DevCenter Project Admin': '/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0' + 'Device Provisioning Service Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633' + 'Device Provisioning Service Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8' + 'Device Update Administrator': '/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a' + 'Device Update Content Administrator': '/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98' + 'Device Update Content Reader': '/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b' + 'Device Update Deployments Administrator': '/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432' + 'Device Update Deployments Reader': '/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f' + 'Device Update Reader': '/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f' + 'DevTest Labs User': '/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64' + 'DICOM Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8' + 'DICOM Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a' + 'Disk Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24' + 'Disk Pool Operator': '/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840' + 'Disk Restore Operator': '/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13' + 'Disk Snapshot Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce' + 'DNS Resolver Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d' + 'DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314' + 'DocumentDB Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' + 'Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2' + 'Domain Services Reader': '/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb' + 'Elastic SAN Owner': '/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406' + 'Elastic SAN Reader': '/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca' + 'Elastic SAN Volume Group Owner': '/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23' + 'EventGrid Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de' + 'EventGrid Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7' + 'EventGrid EventSubscription Contributor': '/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443' + 'EventGrid EventSubscription Reader': '/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405' + 'Experimentation Administrator': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c' + 'Experimentation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c' + 'Experimentation Metric Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0' + 'Experimentation Reader': '/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1' + 'FHIR Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd' + 'FHIR Data Converter': '/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24' + 'FHIR Data Exporter': '/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843' + 'FHIR Data Importer': '/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b' + 'FHIR Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508' + 'FHIR Data Writer': '/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913' + 'FHIR SMART User': '/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0' + 'Grafana Admin': '/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41' + 'Grafana Editor': '/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f' + 'Grafana Viewer': '/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769' + 'Graph Owner': '/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9' + 'Guest Configuration Resource Contributor': '/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31' + 'HDInsight Cluster Operator': '/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a' + 'HDInsight Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c' + 'Hierarchy Settings Administrator': '/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d' + 'Hybrid Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb' + 'Hybrid Server Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624' + 'Impact Reader': '/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e' + 'Impact Reporter': '/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a' + 'Integration Service Environment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8' + 'Integration Service Environment Developer': '/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec' + 'Intelligent Systems Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e' + 'IoT Hub Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f' + 'IoT Hub Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3' + 'IoT Hub Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47' + 'IoT Hub Twin Contributor': '/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c' + 'Key Vault Administrator': '/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483' + 'Key Vault Certificates Officer': '/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985' + 'Key Vault Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395' + 'Key Vault Crypto Officer': '/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603' + 'Key Vault Crypto Service Encryption User': '/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6' + 'Key Vault Crypto User': '/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424' + 'Key Vault Reader': '/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2' + 'Key Vault Secrets Officer': '/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7' + 'Key Vault Secrets User': '/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6' + 'Knowledge Consumer': '/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c' + 'Kubernetes Agentless Operator': '/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6' + 'Kubernetes Cluster - Azure Arc Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41' + 'Kubernetes Extension Contributor': '/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717' + 'Kubernetes Namespace User': '/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a' + 'Lab Assistant': '/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1' + 'Lab Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270' + 'Lab Creator': '/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead' + 'Lab Operator': '/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d' + 'Lab Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f' + 'Lab Services Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc' + 'Load Test Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e' + 'Load Test Owner': '/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6' + 'Load Test Reader': '/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081' + 'LocalNGFirewallAdministrator role': '/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2' + 'LocalRulestacksAdministrator role': '/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20' + 'Log Analytics Contributor': '/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293' + 'Log Analytics Reader': '/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893' + 'Logic App Contributor': '/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e' + 'Logic App Operator': '/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe' + 'Managed Application Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e' + 'Managed Application Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae' + 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' + 'Managed HSM contributor': '/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d' + 'Managed Identity Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59' + 'Managed Identity Operator': '/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830' + 'Managed Services Registration assignment Delete Role': '/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46' + 'Management Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c' + 'Management Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d' + 'Media Services Account Administrator': '/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466' + 'Media Services Live Events Administrator': '/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77' + 'Media Services Media Operator': '/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c' + 'Media Services Policy Administrator': '/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae' + 'Media Services Streaming Endpoints Administrator': '/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804' + 'Microsoft Sentinel Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a' + 'Microsoft Sentinel Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade' + 'Microsoft Sentinel Playbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5' + 'Microsoft Sentinel Reader': '/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb' + 'Microsoft Sentinel Responder': '/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056' + 'Microsoft.Kubernetes connected cluster role': '/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f' + 'Monitoring Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa' + 'Monitoring Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136' + 'Monitoring Metrics Publisher': '/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb' + 'Monitoring Reader': '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05' + 'MySQL Backup And Export Operator': '/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0' + 'Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + 'New Relic APM Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237' + 'Object Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b' + 'Object Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9' + 'Object Understanding Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745' + 'Object Understanding Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6' + Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + 'PlayFab Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726' + 'PlayFab Reader': '/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf' + 'Policy Insights Data Writer (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84' + 'Private DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f' + 'Project Babylon Data Curator': '/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889' + 'Project Babylon Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446' + 'Project Babylon Data Source Administrator': '/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f' + 'Purview role 1 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347' + 'Purview role 2 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803' + 'Purview role 3 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db' + 'Quota Request Operator': '/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125' + Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' + 'Reader and Data Access': '/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349' + 'Redis Cache Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17' + 'Remote Rendering Administrator': '/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e' + 'Remote Rendering Client': '/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a' + 'Reservation Purchaser': '/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689' + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Scheduled Patching Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6' + 'Scheduler Job Collections Contributor': '/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94' + 'Schema Registry Contributor (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25' + 'Schema Registry Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2' + 'Search Index Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7' + 'Search Index Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f' + 'Search Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + 'Security Admin': '/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd' + 'Security Assessment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5' + 'Security Detonation Chamber Publisher': '/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500' + 'Security Detonation Chamber Reader': '/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5' + 'Security Detonation Chamber Submission Manager': '/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce' + 'Security Detonation Chamber Submitter': '/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0' + 'Security Manager (Legacy)': '/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10' + 'Security Reader': '/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4' + 'Services Hub Operator': '/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b' + 'SignalR AccessKey Reader': '/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e' + 'SignalR App Server': '/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7' + 'SignalR REST API Owner': '/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521' + 'SignalR REST API Reader': '/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035' + 'SignalR Service Owner': '/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3' + 'SignalR/Web PubSub Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761' + 'Site Recovery Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567' + 'Site Recovery Operator': '/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca' + 'Site Recovery Reader': '/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149' + 'Spatial Anchors Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827' + 'Spatial Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c' + 'Spatial Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413' + 'SQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec' + 'SQL Managed Instance Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d' + 'SQL Security Manager': '/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3' + 'SQL Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437' + 'SqlDb Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57' + 'SqlMI Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872' + 'SqlVM Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d' + 'Storage Account Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1' + 'Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab' + 'Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12' + 'Storage Blob Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' + 'Storage Blob Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + 'Storage Blob Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + 'Storage Blob Delegator': '/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a' + 'Storage File Data SMB Share Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb' + 'Storage File Data SMB Share Elevated Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7' + 'Storage File Data SMB Share Reader': '/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314' + 'Storage Queue Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88' + 'Storage Queue Data Message Processor': '/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed' + 'Storage Queue Data Message Sender': '/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' + 'Storage Queue Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925' + 'Storage Table Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' + 'Storage Table Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6' + 'Stream Analytics Query Tester': '/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf' + 'Support Request Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e' + 'Tag Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + 'Template Spec Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b' + 'Template Spec Reader': '/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e' + 'Test Base Reader': '/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85' + 'Traffic Manager Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7' + 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + 'Video Indexer Restricted Viewer': '/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb' + 'Virtual Machine Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4' + 'Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + 'Virtual Machine Local User Login': '/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525' + 'Virtual Machine User Login': '/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52' + 'VM Scanner Operator': '/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd' + 'Web Plan Contributor': '/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b' + 'Web PubSub Service Owner (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4' + 'Web PubSub Service Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf' + 'Website Contributor': '/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772' + 'Windows Admin Center Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f' + 'Workbook Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad' + 'Workbook Reader': '/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d' + 'WorkloadBuilder Migration Agent Role': '/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c' +} + +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +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/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md new file mode 100644 index 000000000..ac582ef4e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/readme.md @@ -0,0 +1,52 @@ +# Role Assignment on Resource Group level `[Microsoft.Authorization/roleAssignments/resourceGroup]` + +With this module you can perform role assignments on a resource group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | You can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | +| `resourceGroupName` | string | `[resourceGroup().name]` | | Name of the Resource Group to assign the RBAC role to. If not provided, will use the current scope for deployment. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Assignment. | +| `resourceGroupName` | string | The name of the resource group the role assignment was applied at. | +| `resourceId` | string | The resource ID of the Role Assignment. | +| `scope` | string | The scope this Role Assignment applies to. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep new file mode 100644 index 000000000..5c99c97dd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/deploy.bicep @@ -0,0 +1,478 @@ +targetScope = 'subscription' + +@sys.description('Required. You can provide either the display name of the role definition, 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. Location deployment metadata.') +param location string = deployment().location + +@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 telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var builtInRoleNames = { + 'Access Review Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/76cc9ee4-d5d3-4a45-a930-26add3d73475' + AcrDelete: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + AcrImageSigner: '/providers/Microsoft.Authorization/roleDefinitions/6cef56e8-d556-48e5-a04f-b8e64114680f' + AcrPull: '/providers/Microsoft.Authorization/roleDefinitions/7f951dda-4ed3-4680-a7ca-43fe172d538d' + AcrPush: '/providers/Microsoft.Authorization/roleDefinitions/8311e382-0749-4cb8-b61a-304f252e45ec' + AcrQuarantineReader: '/providers/Microsoft.Authorization/roleDefinitions/cdda3590-29a3-44f6-95f2-9f980659eb04' + AcrQuarantineWriter: '/providers/Microsoft.Authorization/roleDefinitions/c8d4ff99-41c3-41a8-9f60-21dfdad59608' + 'AgFood Platform Sensor Partner Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6b77f0a0-0d89-41cc-acd1-579c22c17a67' + 'AgFood Platform Service Admin': '/providers/Microsoft.Authorization/roleDefinitions/f8da80de-1ff9-4747-ad80-a19b7f6079e3' + 'AgFood Platform Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8508508a-4469-4e45-963b-2518ee0bb728' + 'AgFood Platform Service Reader': '/providers/Microsoft.Authorization/roleDefinitions/7ec7ccdc-f61e-41fe-9aaf-980df0a44eba' + 'AnyBuild Builder': '/providers/Microsoft.Authorization/roleDefinitions/a2138dac-4907-4679-a376-736901ed8ad8' + 'API Management Developer Portal Content Editor': '/providers/Microsoft.Authorization/roleDefinitions/c031e6a8-4391-4de0-8d69-4706a7ed3729' + 'API Management Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/312a565d-c81f-4fd8-895a-4e21e48d571c' + 'API Management Service Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/e022efe7-f5ba-4159-bbe4-b44f577e9b61' + 'API Management Service Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/71522526-b88f-4d52-b57f-d31fc3546d0d' + 'App Configuration Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b' + 'App Configuration Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071' + 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' + 'Application Insights Component Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ae349356-3a1b-4a5e-921d-050484c6347e' + 'Application Insights Snapshot Debugger': '/providers/Microsoft.Authorization/roleDefinitions/08954f03-6346-4c2e-81c0-ec3a5cfae23b' + 'Attestation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/bbf86eb8-f7b4-4cce-96e4-18cddf81d86e' + 'Attestation Reader': '/providers/Microsoft.Authorization/roleDefinitions/fd1bd22b-8476-40bc-a0bc-69b95687b9f3' + 'Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f353d9bd-d4a6-484e-a77a-8050b599b867' + 'Automation Job Operator': '/providers/Microsoft.Authorization/roleDefinitions/4fe576fe-1146-4730-92eb-48519fa6bf9f' + 'Automation Operator': '/providers/Microsoft.Authorization/roleDefinitions/d3881f73-407a-4167-8283-e981cbba0404' + 'Automation Runbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/5fb5aef8-1081-4b8e-bb16-9d5d0385bab5' + 'Autonomous Development Platform Data Contributor (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/b8b15564-4fa6-4a59-ab12-03e1d9594795' + 'Autonomous Development Platform Data Owner (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/27f8b550-c507-4db9-86f2-f4b8e816d59d' + 'Autonomous Development Platform Data Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/d63b75f7-47ea-4f27-92ac-e0d173aaf093' + 'Avere Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4f8fab4f-1852-4a58-a46a-8eaf358af14a' + 'Avere Operator': '/providers/Microsoft.Authorization/roleDefinitions/c025889f-8102-4ebf-b32c-fc0c6f0c6bd9' + 'Azure Arc Enabled Kubernetes Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/00493d72-78f6-4148-b6c5-d3ce8e4799dd' + 'Azure Arc Kubernetes Admin': '/providers/Microsoft.Authorization/roleDefinitions/dffb1e0c-446f-4dde-a09f-99eb5cc68b96' + 'Azure Arc Kubernetes Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/8393591c-06b9-48a2-a542-1bd6b377f6a2' + 'Azure Arc Kubernetes Viewer': '/providers/Microsoft.Authorization/roleDefinitions/63f0a09d-1495-4db4-a681-037d84835eb4' + 'Azure Arc Kubernetes Writer': '/providers/Microsoft.Authorization/roleDefinitions/5b999177-9696-4545-85c7-50de3797e5a1' + 'Azure Arc ScVmm Administrator role': '/providers/Microsoft.Authorization/roleDefinitions/a92dfd61-77f9-4aec-a531-19858b406c87' + 'Azure Arc ScVmm Private Cloud User': '/providers/Microsoft.Authorization/roleDefinitions/c0781e91-8102-4553-8951-97c6d4243cda' + 'Azure Arc ScVmm Private Clouds Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9' + 'Azure Arc ScVmm VM Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e582369a-e17b-42a5-b10c-874c387c530b' + 'Azure Arc VMware Administrator role ': '/providers/Microsoft.Authorization/roleDefinitions/ddc140ed-e463-4246-9145-7c664192013f' + 'Azure Arc VMware Private Cloud User': '/providers/Microsoft.Authorization/roleDefinitions/ce551c02-7c42-47e0-9deb-e3b6fc3a9a83' + 'Azure Arc VMware Private Clouds Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/67d33e57-3129-45e6-bb0b-7cc522f762fa' + 'Azure Arc VMware VM Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b748a06d-6150-4f8a-aaa9-ce3940cd96cb' + 'Azure Center for SAP solutions administrator': '/providers/Microsoft.Authorization/roleDefinitions/7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7' + 'Azure Center for SAP solutions Management role': '/providers/Microsoft.Authorization/roleDefinitions/6d949e1d-41e2-46e3-8920-c6e4f31a8310' + 'Azure Center for SAP solutions reader': '/providers/Microsoft.Authorization/roleDefinitions/05352d14-a920-4328-a0de-4cbe7430e26b' + 'Azure Center for SAP solutions service role': '/providers/Microsoft.Authorization/roleDefinitions/aabbc5dd-1af0-458b-a942-81af88f9c138' + 'Azure Center for SAP solutions Service role for management': '/providers/Microsoft.Authorization/roleDefinitions/0105a6b0-4bb9-43d2-982a-12806f9faddb' + 'Azure Connected Machine Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/b64e21ea-ac4e-4cdf-9dc9-5b892992bee7' + 'Azure Connected Machine Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cd570a14-e51a-42ad-bac8-bafd67325302' + 'Azure Connected Machine Resource Manager': '/providers/Microsoft.Authorization/roleDefinitions/f5819b54-e033-4d82-ac66-4fec3cbf3f4c' + 'Azure Connected SQL Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/e8113dce-c529-4d33-91fa-e9b972617508' + 'Azure Digital Twins Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/bcd981a7-7f74-457b-83e1-cceb9e632ffe' + 'Azure Digital Twins Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/d57506d4-4c8d-48b1-8587-93c323f6a5a3' + 'Azure Event Hubs Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/f526a384-b230-433a-b45c-95f59c4a2dec' + 'Azure Event Hubs Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/a638d3c7-ab3a-418d-83e6-5f17a39d4fde' + 'Azure Event Hubs Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/2b629674-e913-4c01-ae53-ef4638d8f975' + 'Azure Extension for SQL Server Deployment': '/providers/Microsoft.Authorization/roleDefinitions/7392c568-9289-4bde-aaaa-b7131215889d' + 'Azure Front Door Domain Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0ab34830-df19-4f8c-b84e-aa85b8afa6e8' + 'Azure Front Door Domain Reader': '/providers/Microsoft.Authorization/roleDefinitions/0f99d363-226e-4dca-9920-b807cf8e1a5f' + 'Azure Front Door Secret Contributor': '/providers/Microsoft.Authorization/roleDefinitions/3f2eb865-5811-4578-b90a-6fc6fa0df8e5' + 'Azure Front Door Secret Reader': '/providers/Microsoft.Authorization/roleDefinitions/0db238c4-885e-4c4f-a933-aa2cef684fca' + 'Azure Kubernetes Fleet Manager Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/63bb64ad-9799-4770-b5c3-24ed299a07bf' + 'Azure Kubernetes Fleet Manager RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/434fb43a-c01c-447e-9f67-c3ad923cfaba' + 'Azure Kubernetes Fleet Manager RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/18ab4d3d-a1bf-4477-8ad9-8359bc988f69' + 'Azure Kubernetes Fleet Manager RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/30b27cfc-9c84-438e-b0ce-70e35255df80' + 'Azure Kubernetes Fleet Manager RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/5af6afb3-c06c-4fa4-8848-71a8aee05683' + 'Azure Kubernetes Service Cluster Admin Role': '/providers/Microsoft.Authorization/roleDefinitions/0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8' + 'Azure Kubernetes Service Cluster Monitoring User': '/providers/Microsoft.Authorization/roleDefinitions/1afdec4b-e479-420e-99e7-f82237c7c5e6' + 'Azure Kubernetes Service Cluster User Role': '/providers/Microsoft.Authorization/roleDefinitions/4abbcc35-e782-43d8-92c5-2d3f1bd2253f' + 'Azure Kubernetes Service Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8' + 'Azure Kubernetes Service Policy Add-on Deployment': '/providers/Microsoft.Authorization/roleDefinitions/18ed5180-3e48-46fd-8541-4ea054d57064' + 'Azure Kubernetes Service RBAC Admin': '/providers/Microsoft.Authorization/roleDefinitions/3498e952-d568-435e-9b2c-8d77e338d7f7' + 'Azure Kubernetes Service RBAC Cluster Admin': '/providers/Microsoft.Authorization/roleDefinitions/b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b' + 'Azure Kubernetes Service RBAC Reader': '/providers/Microsoft.Authorization/roleDefinitions/7f6c6a51-bcf8-42ba-9220-52d62157d7db' + 'Azure Kubernetes Service RBAC Writer': '/providers/Microsoft.Authorization/roleDefinitions/a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb' + 'Azure Maps Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dba33070-676a-4fb0-87fa-064dc56ff7fb' + 'Azure Maps Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8f5e0ce6-4f7b-4dcf-bddf-e6f48634a204' + 'Azure Maps Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/423170ca-a8f6-4b0f-8487-9e4eb8f49bfa' + 'Azure Maps Search and Render Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/6be48352-4f82-47c9-ad5e-0acacefdb005' + 'Azure Relay Listener': '/providers/Microsoft.Authorization/roleDefinitions/26e0b698-aa6d-4085-9386-aadae190014d' + 'Azure Relay Owner': '/providers/Microsoft.Authorization/roleDefinitions/2787bf04-f1f5-4bfe-8383-c8a24483ee38' + 'Azure Relay Sender': '/providers/Microsoft.Authorization/roleDefinitions/26baccc8-eea7-41f1-98f4-1762cc7f685d' + 'Azure Service Bus Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/090c5cfd-751d-490a-894a-3ce6f1109419' + 'Azure Service Bus Data Receiver': '/providers/Microsoft.Authorization/roleDefinitions/4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' + 'Azure Service Bus Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' + 'Azure Spring Apps Connect Role': '/providers/Microsoft.Authorization/roleDefinitions/80558df3-64f9-4c0f-b32d-e5094b036b0b' + 'Azure Spring Apps Remote Debugging Role': '/providers/Microsoft.Authorization/roleDefinitions/a99b0159-1064-4c22-a57b-c9b3caa1c054' + 'Azure Spring Cloud Config Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a06f5c24-21a7-4e1a-aa2b-f19eb6684f5b' + 'Azure Spring Cloud Config Server Reader': '/providers/Microsoft.Authorization/roleDefinitions/d04c6db6-4947-4782-9e91-30a88feb7be7' + 'Azure Spring Cloud Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b5537268-8956-4941-a8f0-646150406f0c' + 'Azure Spring Cloud Service Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f5880b48-c26d-48be-b172-7927bfa1c8f1' + 'Azure Spring Cloud Service Registry Reader': '/providers/Microsoft.Authorization/roleDefinitions/cff1b556-2399-4e7e-856d-a8f754be7b65' + 'Azure Stack HCI registration role': '/providers/Microsoft.Authorization/roleDefinitions/bda0d508-adf1-4af0-9c28-88919fc3ae06' + 'Azure Stack Registration Owner': '/providers/Microsoft.Authorization/roleDefinitions/6f12a6df-dd06-4f3e-bcb1-ce8be600526a' + 'Azure Traffic Controller Configuration Manager': '/providers/Microsoft.Authorization/roleDefinitions/fbc52c3f-28ad-4303-a892-8a056630b8f1' + 'Azure Usage Billing Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/f0310ce6-e953-4cf8-b892-fb1c87eaf7f6' + 'Azure VM Managed identities restore Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6ae96244-5829-4925-a7d3-5975537d91dd' + 'AzureML Compute Operator': '/providers/Microsoft.Authorization/roleDefinitions/e503ece1-11d0-4e8e-8e2c-7a6c3bf38815' + 'AzureML Data Scientist': '/providers/Microsoft.Authorization/roleDefinitions/f6c7c914-8db3-469d-8ca1-694a8f32e121' + 'AzureML Metrics Writer (preview)': '/providers/Microsoft.Authorization/roleDefinitions/635dd51f-9968-44d3-b7fb-6d9a6bd613ae' + 'AzureML Registry User': '/providers/Microsoft.Authorization/roleDefinitions/1823dd4f-9b8c-4ab6-ab4e-7397a3684615' + 'Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e467623-bb1f-42f4-a55d-6e525e11384b' + 'Backup Operator': '/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324' + 'Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/a795c7a0-d4a2-40c1-ae25-d81f01202912' + 'Bayer Ag Powered Services CWUM Solution User Role': '/providers/Microsoft.Authorization/roleDefinitions/a9b99099-ead7-47db-8fcf-072597a61dfa' + 'Bayer Ag Powered Services GDU Solution': '/providers/Microsoft.Authorization/roleDefinitions/c4bc862a-3b64-4a35-a021-a380c159b042' + 'Bayer Ag Powered Services Imagery Solution': '/providers/Microsoft.Authorization/roleDefinitions/ef29765d-0d37-4119-a4f8-f9f9902c9588' + 'Billing Reader': '/providers/Microsoft.Authorization/roleDefinitions/fa23ad8b-c56e-40d8-ac0c-ce449e1d2c64' + 'BizTalk Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5e3c6656-6cfa-4708-81fe-0de47ac73342' + 'Blockchain Member Node Access (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/31a002a1-acaf-453e-8a5b-297c9ca1ea24' + 'Blueprint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/41077137-e803-4205-871c-5a86e6a753b4' + 'Blueprint Operator': '/providers/Microsoft.Authorization/roleDefinitions/437d2ced-4a38-4302-8479-ed2bcb43d090' + 'CDN Endpoint Contributor': '/providers/Microsoft.Authorization/roleDefinitions/426e0c7f-0c7e-4658-b36f-ff54d6c29b45' + 'CDN Endpoint Reader': '/providers/Microsoft.Authorization/roleDefinitions/871e35f6-b5c1-49cc-a043-bde969a0f2cd' + 'CDN Profile Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ec156ff8-a8d1-4d15-830c-5b80698ca432' + 'CDN Profile Reader': '/providers/Microsoft.Authorization/roleDefinitions/8f96442b-4075-438f-813d-ad51ab4019af' + 'Chamber Admin': '/providers/Microsoft.Authorization/roleDefinitions/4e9b8407-af2e-495b-ae54-bb60a55b1b5a' + 'Chamber User': '/providers/Microsoft.Authorization/roleDefinitions/4447db05-44ed-4da3-ae60-6cbece780e32' + 'Classic Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b34d265f-36f7-4a0d-a4d4-e158ca92e90f' + 'Classic Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86e8f5dc-a6e9-4c67-9d15-de283e8eac25' + 'Classic Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/985d6b00-f706-48f5-a6fe-d0ca12fb668d' + 'Classic Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/d73bb868-a0df-4d4d-bd69-98a00b01fccb' + 'ClearDB MySQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9106cda0-8a86-4e81-b686-29a22c54effe' + 'Code Signing Certificate Profile Signer': '/providers/Microsoft.Authorization/roleDefinitions/2837e146-70d7-4cfd-ad55-7efa6464f958' + 'Code Signing Identity Verifier': '/providers/Microsoft.Authorization/roleDefinitions/4339b7cf-9826-4e41-b4ed-c7f4505dac08' + 'Cognitive Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + 'Cognitive Services Custom Vision Contributor': '/providers/Microsoft.Authorization/roleDefinitions/c1ff6cc2-c111-46fe-8896-e0ef812ad9f3' + 'Cognitive Services Custom Vision Deployment': '/providers/Microsoft.Authorization/roleDefinitions/5c4089e1-6d96-4d2f-b296-c1bc7137275f' + 'Cognitive Services Custom Vision Labeler': '/providers/Microsoft.Authorization/roleDefinitions/88424f51-ebe7-446f-bc41-7fa16989e96c' + 'Cognitive Services Custom Vision Reader': '/providers/Microsoft.Authorization/roleDefinitions/93586559-c37d-4a6b-ba08-b9f0940c2d73' + 'Cognitive Services Custom Vision Trainer': '/providers/Microsoft.Authorization/roleDefinitions/0a5ae4ab-0d65-4eeb-be61-29fc9b54394b' + 'Cognitive Services Data Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/b59867f0-fa02-499b-be73-45a86b5b3e1c' + 'Cognitive Services Face Recognizer': '/providers/Microsoft.Authorization/roleDefinitions/9894cab4-e18a-44aa-828b-cb588cd6f2d7' + 'Cognitive Services Immersive Reader User': '/providers/Microsoft.Authorization/roleDefinitions/b2de6794-95db-4659-8781-7e080d3f2b9d' + 'Cognitive Services Language Owner': '/providers/Microsoft.Authorization/roleDefinitions/f07febfe-79bc-46b1-8b37-790e26e6e498' + 'Cognitive Services Language Reader': '/providers/Microsoft.Authorization/roleDefinitions/7628b7b8-a8b2-4cdc-b46f-e9b35248918e' + 'Cognitive Services Language Writer': '/providers/Microsoft.Authorization/roleDefinitions/f2310ca1-dc64-4889-bb49-c8e0fa3d47a8' + 'Cognitive Services LUIS Owner': '/providers/Microsoft.Authorization/roleDefinitions/f72c8140-2111-481c-87ff-72b910f6e3f8' + 'Cognitive Services LUIS Reader': '/providers/Microsoft.Authorization/roleDefinitions/18e81cdc-4e98-4e29-a639-e7d10c5a6226' + 'Cognitive Services LUIS Writer': '/providers/Microsoft.Authorization/roleDefinitions/6322a993-d5c9-4bed-b113-e49bbea25b27' + 'Cognitive Services Metrics Advisor Administrator': '/providers/Microsoft.Authorization/roleDefinitions/cb43c632-a144-4ec5-977c-e80c4affc34a' + 'Cognitive Services Metrics Advisor User': '/providers/Microsoft.Authorization/roleDefinitions/3b20f47b-3825-43cb-8114-4bd2201156a8' + 'Cognitive Services OpenAI Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a001fd3d-188f-4b5d-821b-7da978bf7442' + 'Cognitive Services OpenAI User': '/providers/Microsoft.Authorization/roleDefinitions/5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + 'Cognitive Services QnA Maker Editor': '/providers/Microsoft.Authorization/roleDefinitions/f4cc2bf9-21be-47a1-bdf1-5c5804381025' + 'Cognitive Services QnA Maker Reader': '/providers/Microsoft.Authorization/roleDefinitions/466ccd10-b268-4a11-b098-b4849f024126' + 'Cognitive Services Speech Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0e75ca1e-0464-4b4d-8b93-68208a576181' + 'Cognitive Services Speech User': '/providers/Microsoft.Authorization/roleDefinitions/f2dc8367-1007-4938-bd23-fe263f013447' + 'Cognitive Services User': '/providers/Microsoft.Authorization/roleDefinitions/a97b65f3-24c7-4388-baec-2e87135dc908' + 'Collaborative Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/daa9e50b-21df-454c-94a6-a8050adab352' + 'Collaborative Runtime Operator': '/providers/Microsoft.Authorization/roleDefinitions/7a6f0e70-c033-4fb1-828c-08514e5f4102' + 'Compute Gallery Sharing Admin': '/providers/Microsoft.Authorization/roleDefinitions/1ef6a3be-d0ac-425d-8c01-acb62866290b' + 'ContainerApp Reader': '/providers/Microsoft.Authorization/roleDefinitions/ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b' + Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' + 'Cosmos DB Account Reader Role': '/providers/Microsoft.Authorization/roleDefinitions/fbdf93bf-df7d-467e-a4d2-9458aa1360c8' + 'Cosmos DB Operator': '/providers/Microsoft.Authorization/roleDefinitions/230815da-be43-4aae-9cb4-875f7bd000aa' + CosmosBackupOperator: '/providers/Microsoft.Authorization/roleDefinitions/db7b14f2-5adf-42da-9f96-f2ee17bab5cb' + CosmosRestoreOperator: '/providers/Microsoft.Authorization/roleDefinitions/5432c526-bc82-444a-b7ba-57c5b0b5b34f' + 'Cost Management Contributor': '/providers/Microsoft.Authorization/roleDefinitions/434105ed-43f6-45c7-a02f-909b2ba83430' + 'Cost Management Reader': '/providers/Microsoft.Authorization/roleDefinitions/72fafb9e-0641-4937-9268-a91bfd8191a3' + 'Data Box Contributor': '/providers/Microsoft.Authorization/roleDefinitions/add466c9-e687-43fc-8d98-dfcf8d720be5' + 'Data Box Reader': '/providers/Microsoft.Authorization/roleDefinitions/028f4ed7-e2a9-465e-a8f4-9c0ffdfdc027' + 'Data Factory Contributor': '/providers/Microsoft.Authorization/roleDefinitions/673868aa-7521-48a0-acc6-0f60742d39f5' + 'Data Labeling - Labeler': '/providers/Microsoft.Authorization/roleDefinitions/c6decf44-fd0a-444c-a844-d653c394e7ab' + 'Data Lake Analytics Developer': '/providers/Microsoft.Authorization/roleDefinitions/47b7735b-770e-4598-a7da-8b91488b4c88' + 'Data Operator for Managed Disks': '/providers/Microsoft.Authorization/roleDefinitions/959f8984-c045-4866-89c7-12bf9737be2e' + 'Data Purger': '/providers/Microsoft.Authorization/roleDefinitions/150f5e0c-0603-4f03-8c7f-cf70034c4e90' + 'Deployment Environments User': '/providers/Microsoft.Authorization/roleDefinitions/18e40d4e-8d2e-438d-97e1-9528336e149c' + 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' + 'Desktop Virtualization Application Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/aebf23d0-b568-4e86-b8f9-fe83a2c6ab55' + 'Desktop Virtualization Contributor': '/providers/Microsoft.Authorization/roleDefinitions/082f0a83-3be5-4ba1-904c-961cca79b387' + 'Desktop Virtualization Host Pool Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e307426c-f9b6-4e81-87de-d99efb3c32bc' + 'Desktop Virtualization Host Pool Reader': '/providers/Microsoft.Authorization/roleDefinitions/ceadfde2-b300-400a-ab7b-6143895aa822' + 'Desktop Virtualization Power On Contributor': '/providers/Microsoft.Authorization/roleDefinitions/489581de-a3bd-480d-9518-53dea7416b33' + 'Desktop Virtualization Power On Off Contributor': '/providers/Microsoft.Authorization/roleDefinitions/40c5ff49-9181-41f8-ae61-143b0e78555e' + 'Desktop Virtualization Reader': '/providers/Microsoft.Authorization/roleDefinitions/49a72310-ab8d-41df-bbb0-79b649203868' + 'Desktop Virtualization Session Host Operator': '/providers/Microsoft.Authorization/roleDefinitions/2ad6aaab-ead9-4eaa-8ac5-da422f562408' + 'Desktop Virtualization User': '/providers/Microsoft.Authorization/roleDefinitions/1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' + 'Desktop Virtualization User Session Operator': '/providers/Microsoft.Authorization/roleDefinitions/ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6' + 'Desktop Virtualization Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a959dbd1-f747-45e3-8ba6-dd80f235f97c' + 'Desktop Virtualization Workspace Contributor': '/providers/Microsoft.Authorization/roleDefinitions/21efdde3-836f-432b-bf3d-3e8e734d4b2b' + 'Desktop Virtualization Workspace Reader': '/providers/Microsoft.Authorization/roleDefinitions/0fa44ee9-7a7d-466b-9bb2-2bf446b1204d' + 'DevCenter Dev Box User': '/providers/Microsoft.Authorization/roleDefinitions/45d50f46-0b78-4001-a660-4198cbe8cd05' + 'DevCenter Project Admin': '/providers/Microsoft.Authorization/roleDefinitions/331c37c6-af14-46d9-b9f4-e1909e1b95a0' + 'Device Provisioning Service Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/dfce44e4-17b7-4bd1-a6d1-04996ec95633' + 'Device Provisioning Service Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/10745317-c249-44a1-a5ce-3a4353c0bbd8' + 'Device Update Administrator': '/providers/Microsoft.Authorization/roleDefinitions/02ca0879-e8e4-47a5-a61e-5c618b76e64a' + 'Device Update Content Administrator': '/providers/Microsoft.Authorization/roleDefinitions/0378884a-3af5-44ab-8323-f5b22f9f3c98' + 'Device Update Content Reader': '/providers/Microsoft.Authorization/roleDefinitions/d1ee9a80-8b14-47f0-bdc2-f4a351625a7b' + 'Device Update Deployments Administrator': '/providers/Microsoft.Authorization/roleDefinitions/e4237640-0e3d-4a46-8fda-70bc94856432' + 'Device Update Deployments Reader': '/providers/Microsoft.Authorization/roleDefinitions/49e2f5d2-7741-4835-8efa-19e1fe35e47f' + 'Device Update Reader': '/providers/Microsoft.Authorization/roleDefinitions/e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f' + 'DevTest Labs User': '/providers/Microsoft.Authorization/roleDefinitions/76283e04-6283-4c54-8f91-bcf1374a3c64' + 'DICOM Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/58a3b984-7adf-4c20-983a-32417c86fbc8' + 'DICOM Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/e89c7a3c-2f64-4fa1-a847-3e4c9ba4283a' + 'Disk Backup Reader': '/providers/Microsoft.Authorization/roleDefinitions/3e5e47e6-65f7-47ef-90b5-e5dd4d455f24' + 'Disk Pool Operator': '/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840' + 'Disk Restore Operator': '/providers/Microsoft.Authorization/roleDefinitions/b50d9833-a0cb-478e-945f-707fcc997c13' + 'Disk Snapshot Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7efff54f-a5b4-42b5-a1c5-5411624893ce' + 'DNS Resolver Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d' + 'DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/befefa01-2a29-4197-83a8-272ff33ce314' + 'DocumentDB Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' + 'Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/eeaeda52-9324-47f6-8069-5d5bade478b2' + 'Domain Services Reader': '/providers/Microsoft.Authorization/roleDefinitions/361898ef-9ed1-48c2-849c-a832951106bb' + 'Elastic SAN Owner': '/providers/Microsoft.Authorization/roleDefinitions/80dcbedb-47ef-405d-95bd-188a1b4ac406' + 'Elastic SAN Reader': '/providers/Microsoft.Authorization/roleDefinitions/af6a70f8-3c9f-4105-acf1-d719e9fca4ca' + 'Elastic SAN Volume Group Owner': '/providers/Microsoft.Authorization/roleDefinitions/a8281131-f312-4f34-8d98-ae12be9f0d23' + 'EventGrid Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1e241071-0855-49ea-94dc-649edcd759de' + 'EventGrid Data Sender': '/providers/Microsoft.Authorization/roleDefinitions/d5a91429-5739-47e2-a06b-3470a27159e7' + 'EventGrid EventSubscription Contributor': '/providers/Microsoft.Authorization/roleDefinitions/428e0ff0-5e57-4d9c-a221-2c70d0e0a443' + 'EventGrid EventSubscription Reader': '/providers/Microsoft.Authorization/roleDefinitions/2414bbcf-6497-4faf-8c65-045460748405' + 'Experimentation Administrator': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a33b-edd6ce5c915c' + 'Experimentation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7f646f1b-fa08-80eb-a22b-edd6ce5c915c' + 'Experimentation Metric Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6188b7c9-7d01-4f99-a59f-c88b630326c0' + 'Experimentation Reader': '/providers/Microsoft.Authorization/roleDefinitions/49632ef5-d9ac-41f4-b8e7-bbe587fa74a1' + 'FHIR Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5a1fc7df-4bf1-4951-a576-89034ee01acd' + 'FHIR Data Converter': '/providers/Microsoft.Authorization/roleDefinitions/a1705bd2-3a8f-45a5-8683-466fcfd5cc24' + 'FHIR Data Exporter': '/providers/Microsoft.Authorization/roleDefinitions/3db33094-8700-4567-8da5-1501d4e7e843' + 'FHIR Data Importer': '/providers/Microsoft.Authorization/roleDefinitions/4465e953-8ced-4406-a58e-0f6e3f3b530b' + 'FHIR Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/4c8d0bbc-75d3-4935-991f-5f3c56d81508' + 'FHIR Data Writer': '/providers/Microsoft.Authorization/roleDefinitions/3f88fce4-5892-4214-ae73-ba5294559913' + 'FHIR SMART User': '/providers/Microsoft.Authorization/roleDefinitions/4ba50f17-9666-485c-a643-ff00808643f0' + 'Grafana Admin': '/providers/Microsoft.Authorization/roleDefinitions/22926164-76b3-42b3-bc55-97df8dab3e41' + 'Grafana Editor': '/providers/Microsoft.Authorization/roleDefinitions/a79a5197-3a5c-4973-a920-486035ffd60f' + 'Grafana Viewer': '/providers/Microsoft.Authorization/roleDefinitions/60921a7e-fef1-4a43-9b16-a26c52ad4769' + 'Graph Owner': '/providers/Microsoft.Authorization/roleDefinitions/b60367af-1334-4454-b71e-769d9a4f83d9' + 'Guest Configuration Resource Contributor': '/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31' + 'HDInsight Cluster Operator': '/providers/Microsoft.Authorization/roleDefinitions/61ed4efc-fab3-44fd-b111-e24485cc132a' + 'HDInsight Domain Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8d8d5a11-05d3-4bda-a417-a08778121c7c' + 'Hierarchy Settings Administrator': '/providers/Microsoft.Authorization/roleDefinitions/350f8d15-c687-4448-8ae1-157740a3936d' + 'Hybrid Server Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/5d1e5ee4-7c68-4a71-ac8b-0739630a3dfb' + 'Hybrid Server Resource Administrator': '/providers/Microsoft.Authorization/roleDefinitions/48b40c6e-82e0-4eb3-90d5-19e40f49b624' + 'Impact Reader': '/providers/Microsoft.Authorization/roleDefinitions/68ff5d27-c7f5-4fa9-a21c-785d0df7bd9e' + 'Impact Reporter': '/providers/Microsoft.Authorization/roleDefinitions/36e80216-a7e8-4f42-a7e1-f12c98cbaf8a' + 'Integration Service Environment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a41e2c5b-bd99-4a07-88f4-9bf657a760b8' + 'Integration Service Environment Developer': '/providers/Microsoft.Authorization/roleDefinitions/c7aa55d3-1abb-444a-a5ca-5e51e485d6ec' + 'Intelligent Systems Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/03a6d094-3444-4b3d-88af-7477090a9e5e' + 'IoT Hub Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4fc6c259-987e-4a07-842e-c321cc9d413f' + 'IoT Hub Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b447c946-2db7-41ec-983d-d8bf3b1c77e3' + 'IoT Hub Registry Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4ea46cd5-c1b2-4a8e-910b-273211f9ce47' + 'IoT Hub Twin Contributor': '/providers/Microsoft.Authorization/roleDefinitions/494bdba2-168f-4f31-a0a1-191d2f7c028c' + 'Key Vault Administrator': '/providers/Microsoft.Authorization/roleDefinitions/00482a5a-887f-4fb3-b363-3b7fe8e74483' + 'Key Vault Certificates Officer': '/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985' + 'Key Vault Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f25e0fa2-a7c8-4377-a976-54943a77a395' + 'Key Vault Crypto Officer': '/providers/Microsoft.Authorization/roleDefinitions/14b46e9e-c2b7-41b4-b07b-48a6ebf60603' + 'Key Vault Crypto Service Encryption User': '/providers/Microsoft.Authorization/roleDefinitions/e147488a-f6f5-4113-8e2d-b22465e65bf6' + 'Key Vault Crypto User': '/providers/Microsoft.Authorization/roleDefinitions/12338af0-0e69-4776-bea7-57ae8d297424' + 'Key Vault Reader': '/providers/Microsoft.Authorization/roleDefinitions/21090545-7ca7-4776-b22c-e363652d74d2' + 'Key Vault Secrets Officer': '/providers/Microsoft.Authorization/roleDefinitions/b86a8fe4-44ce-4948-aee5-eccb2c155cd7' + 'Key Vault Secrets User': '/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6' + 'Knowledge Consumer': '/providers/Microsoft.Authorization/roleDefinitions/ee361c5d-f7b5-4119-b4b6-892157c8f64c' + 'Kubernetes Agentless Operator': '/providers/Microsoft.Authorization/roleDefinitions/d5a2ae44-610b-4500-93be-660a0c5f5ca6' + 'Kubernetes Cluster - Azure Arc Onboarding': '/providers/Microsoft.Authorization/roleDefinitions/34e09817-6cbe-4d01-b1a2-e0eac5743d41' + 'Kubernetes Extension Contributor': '/providers/Microsoft.Authorization/roleDefinitions/85cb6faf-e071-4c9b-8136-154b5a04f717' + 'Kubernetes Namespace User': '/providers/Microsoft.Authorization/roleDefinitions/ba79058c-0414-4a34-9e42-c3399d80cd5a' + 'Lab Assistant': '/providers/Microsoft.Authorization/roleDefinitions/ce40b423-cede-4313-a93f-9b28290b72e1' + 'Lab Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5daaa2af-1fe8-407c-9122-bba179798270' + 'Lab Creator': '/providers/Microsoft.Authorization/roleDefinitions/b97fb8bc-a8b2-4522-a38b-dd33c7e65ead' + 'Lab Operator': '/providers/Microsoft.Authorization/roleDefinitions/a36e6959-b6be-4b12-8e9f-ef4b474d304d' + 'Lab Services Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f69b8690-cc87-41d6-b77a-a4bc3c0a966f' + 'Lab Services Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc' + 'Load Test Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749a398d-560b-491b-bb21-08924219302e' + 'Load Test Owner': '/providers/Microsoft.Authorization/roleDefinitions/45bb0b16-2f0c-4e78-afaa-a07599b003f6' + 'Load Test Reader': '/providers/Microsoft.Authorization/roleDefinitions/3ae3fb29-0000-4ccd-bf80-542e7b26e081' + 'LocalNGFirewallAdministrator role': '/providers/Microsoft.Authorization/roleDefinitions/a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2' + 'LocalRulestacksAdministrator role': '/providers/Microsoft.Authorization/roleDefinitions/bfc3b73d-c6ff-45eb-9a5f-40298295bf20' + 'Log Analytics Contributor': '/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293' + 'Log Analytics Reader': '/providers/Microsoft.Authorization/roleDefinitions/73c42c96-874c-492b-b04d-ab87d138a893' + 'Logic App Contributor': '/providers/Microsoft.Authorization/roleDefinitions/87a39d53-fc1b-424a-814c-f7e04687dc9e' + 'Logic App Operator': '/providers/Microsoft.Authorization/roleDefinitions/515c2055-d9d4-4321-b1b9-bd0c9a0f79fe' + 'Managed Application Contributor Role': '/providers/Microsoft.Authorization/roleDefinitions/641177b8-a67a-45b9-a033-47bc880bb21e' + 'Managed Application Operator Role': '/providers/Microsoft.Authorization/roleDefinitions/c7393b34-138c-406f-901b-d8cf2b17e6ae' + 'Managed Applications Reader': '/providers/Microsoft.Authorization/roleDefinitions/b9331d33-8a36-4f8c-b097-4f54124fdb44' + 'Managed HSM contributor': '/providers/Microsoft.Authorization/roleDefinitions/18500a29-7fe2-46b2-a342-b16a415e101d' + 'Managed Identity Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e40ec5ca-96e0-45a2-b4ff-59039f2c2b59' + 'Managed Identity Operator': '/providers/Microsoft.Authorization/roleDefinitions/f1a07417-d97a-45cb-824c-7a7467783830' + 'Managed Services Registration assignment Delete Role': '/providers/Microsoft.Authorization/roleDefinitions/91c1777a-f3dc-4fae-b103-61d183457e46' + 'Management Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d58bcaf-24a5-4b20-bdb6-eed9f69fbe4c' + 'Management Group Reader': '/providers/Microsoft.Authorization/roleDefinitions/ac63b705-f282-497d-ac71-919bf39d939d' + 'Media Services Account Administrator': '/providers/Microsoft.Authorization/roleDefinitions/054126f8-9a2b-4f1c-a9ad-eca461f08466' + 'Media Services Live Events Administrator': '/providers/Microsoft.Authorization/roleDefinitions/532bc159-b25e-42c0-969e-a1d439f60d77' + 'Media Services Media Operator': '/providers/Microsoft.Authorization/roleDefinitions/e4395492-1534-4db2-bedf-88c14621589c' + 'Media Services Policy Administrator': '/providers/Microsoft.Authorization/roleDefinitions/c4bba371-dacd-4a26-b320-7250bca963ae' + 'Media Services Streaming Endpoints Administrator': '/providers/Microsoft.Authorization/roleDefinitions/99dba123-b5fe-44d5-874c-ced7199a5804' + 'Microsoft Sentinel Automation Contributor': '/providers/Microsoft.Authorization/roleDefinitions/f4c81013-99ee-4d62-a7ee-b3f1f648599a' + 'Microsoft Sentinel Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ab8e14d6-4a74-4a29-9ba8-549422addade' + 'Microsoft Sentinel Playbook Operator': '/providers/Microsoft.Authorization/roleDefinitions/51d6186e-6489-4900-b93f-92e23144cca5' + 'Microsoft Sentinel Reader': '/providers/Microsoft.Authorization/roleDefinitions/8d289c81-5878-46d4-8554-54e1e3d8b5cb' + 'Microsoft Sentinel Responder': '/providers/Microsoft.Authorization/roleDefinitions/3e150937-b8fe-4cfb-8069-0eaf05ecd056' + 'Microsoft.Kubernetes connected cluster role': '/providers/Microsoft.Authorization/roleDefinitions/5548b2cf-c94c-4228-90ba-30851930a12f' + 'Monitoring Contributor': '/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa' + 'Monitoring Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/b0d8363b-8ddd-447d-831f-62ca05bff136' + 'Monitoring Metrics Publisher': '/providers/Microsoft.Authorization/roleDefinitions/3913510d-42f4-4e42-8a64-420c390055eb' + 'Monitoring Reader': '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05' + 'MySQL Backup And Export Operator': '/providers/Microsoft.Authorization/roleDefinitions/d18ad5f3-1baf-4119-b49b-d944edb1f9d0' + 'Network Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + 'New Relic APM Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/5d28c62d-5b37-4476-8438-e587778df237' + 'Object Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/ca0835dd-bacc-42dd-8ed2-ed5e7230d15b' + 'Object Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/4a167cdf-cb95-4554-9203-2347fe489bd9' + 'Object Understanding Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/4dd61c23-6743-42fe-a388-d8bdd41cb745' + 'Object Understanding Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/d18777c0-1514-4662-8490-608db7d334b6' + Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' + 'PlayFab Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c8b84dc-067c-4039-9615-fa1a4b77c726' + 'PlayFab Reader': '/providers/Microsoft.Authorization/roleDefinitions/a9a19cc5-31f4-447c-901f-56c0bb18fcaf' + 'Policy Insights Data Writer (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/66bb4e9e-b016-4a94-8249-4c0511c2be84' + 'Private DNS Zone Contributor': '/providers/Microsoft.Authorization/roleDefinitions/b12aa53e-6015-4669-85d0-8515ebb3ae7f' + 'Project Babylon Data Curator': '/providers/Microsoft.Authorization/roleDefinitions/9ef4ef9c-a049-46b0-82ab-dd8ac094c889' + 'Project Babylon Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/c8d896ba-346d-4f50-bc1d-7d1c84130446' + 'Project Babylon Data Source Administrator': '/providers/Microsoft.Authorization/roleDefinitions/05b7651b-dc44-475e-b74d-df3db49fae0f' + 'Purview role 1 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/8a3c2885-9b38-4fd2-9d99-91af537c1347' + 'Purview role 2 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/200bba9e-f0c8-430f-892b-6f0794863803' + 'Purview role 3 (Deprecated)': '/providers/Microsoft.Authorization/roleDefinitions/ff100721-1b9d-43d8-af52-42b69c1272db' + 'Quota Request Operator': '/providers/Microsoft.Authorization/roleDefinitions/0e5f05e5-9ab9-446b-b98d-1e2157c94125' + Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' + 'Reader and Data Access': '/providers/Microsoft.Authorization/roleDefinitions/c12c1c16-33a1-487b-954d-41c89c60f349' + 'Redis Cache Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e0f68234-74aa-48ed-b826-c38b57376e17' + 'Remote Rendering Administrator': '/providers/Microsoft.Authorization/roleDefinitions/3df8b902-2a6f-47c7-8cc5-360e9b272a7e' + 'Remote Rendering Client': '/providers/Microsoft.Authorization/roleDefinitions/d39065c4-c120-43c9-ab0a-63eed9795f0a' + 'Reservation Purchaser': '/providers/Microsoft.Authorization/roleDefinitions/f7b75c60-3036-4b75-91c3-6b41c27c1689' + 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' + 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Scheduled Patching Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cd08ab90-6b14-449c-ad9a-8f8e549482c6' + 'Scheduler Job Collections Contributor': '/providers/Microsoft.Authorization/roleDefinitions/188a0f2f-5c9e-469b-ae67-2aa5ce574b94' + 'Schema Registry Contributor (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/5dffeca3-4936-4216-b2bc-10343a5abb25' + 'Schema Registry Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/2c56ea50-c6b3-40a6-83c0-9d98858bc7d2' + 'Search Index Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8ebe5a00-799e-43f5-93ac-243d3dce84a7' + 'Search Index Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/1407120a-92aa-4202-b7e9-c0e197c71c8f' + 'Search Service Contributor': '/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + 'Security Admin': '/providers/Microsoft.Authorization/roleDefinitions/fb1c8493-542b-48eb-b624-b4c8fea62acd' + 'Security Assessment Contributor': '/providers/Microsoft.Authorization/roleDefinitions/612c2aa1-cb24-443b-ac28-3ab7272de6f5' + 'Security Detonation Chamber Publisher': '/providers/Microsoft.Authorization/roleDefinitions/352470b3-6a9c-4686-b503-35deb827e500' + 'Security Detonation Chamber Reader': '/providers/Microsoft.Authorization/roleDefinitions/28241645-39f8-410b-ad48-87863e2951d5' + 'Security Detonation Chamber Submission Manager': '/providers/Microsoft.Authorization/roleDefinitions/a37b566d-3efa-4beb-a2f2-698963fa42ce' + 'Security Detonation Chamber Submitter': '/providers/Microsoft.Authorization/roleDefinitions/0b555d9b-b4a7-4f43-b330-627f0e5be8f0' + 'Security Manager (Legacy)': '/providers/Microsoft.Authorization/roleDefinitions/e3d13bf0-dd5a-482e-ba6b-9b8433878d10' + 'Security Reader': '/providers/Microsoft.Authorization/roleDefinitions/39bc4728-0917-49c7-9d2c-d95423bc2eb4' + 'Services Hub Operator': '/providers/Microsoft.Authorization/roleDefinitions/82200a5b-e217-47a5-b665-6d8765ee745b' + 'SignalR AccessKey Reader': '/providers/Microsoft.Authorization/roleDefinitions/04165923-9d83-45d5-8227-78b77b0a687e' + 'SignalR App Server': '/providers/Microsoft.Authorization/roleDefinitions/420fcaa2-552c-430f-98ca-3264be4806c7' + 'SignalR REST API Owner': '/providers/Microsoft.Authorization/roleDefinitions/fd53cd77-2268-407a-8f46-7e7863d0f521' + 'SignalR REST API Reader': '/providers/Microsoft.Authorization/roleDefinitions/ddde6b66-c0df-4114-a159-3618637b3035' + 'SignalR Service Owner': '/providers/Microsoft.Authorization/roleDefinitions/7e4f1700-ea5a-4f59-8f37-079cfe29dce3' + 'SignalR/Web PubSub Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761' + 'Site Recovery Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6670b86e-a3f7-4917-ac9b-5d6ab1be4567' + 'Site Recovery Operator': '/providers/Microsoft.Authorization/roleDefinitions/494ae006-db33-4328-bf46-533a6560a3ca' + 'Site Recovery Reader': '/providers/Microsoft.Authorization/roleDefinitions/dbaa88c4-0c30-4179-9fb3-46319faa6149' + 'Spatial Anchors Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/8bbe83f1-e2a6-4df7-8cb4-4e04d4e5c827' + 'Spatial Anchors Account Owner': '/providers/Microsoft.Authorization/roleDefinitions/70bbe301-9835-447d-afdd-19eb3167307c' + 'Spatial Anchors Account Reader': '/providers/Microsoft.Authorization/roleDefinitions/5d51204f-eb77-4b1c-b86a-2ec626c49413' + 'SQL DB Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9b7fa17d-e63e-47b0-bb0a-15c516ac86ec' + 'SQL Managed Instance Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4939a1f6-9ae0-4e48-a1e0-f2cbe897382d' + 'SQL Security Manager': '/providers/Microsoft.Authorization/roleDefinitions/056cd41c-7e88-42e1-933e-88ba6a50c9c3' + 'SQL Server Contributor': '/providers/Microsoft.Authorization/roleDefinitions/6d8ee4ec-f05a-4a1d-8b00-a9b17e38b437' + 'SqlDb Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/189207d4-bb67-4208-a635-b06afe8b2c57' + 'SqlMI Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/1d335eef-eee1-47fe-a9e0-53214eba8872' + 'SqlVM Migration Role': '/providers/Microsoft.Authorization/roleDefinitions/ae8036db-e102-405b-a1b9-bae082ea436d' + 'Storage Account Backup Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1' + 'Storage Account Contributor': '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab' + 'Storage Account Key Operator Service Role': '/providers/Microsoft.Authorization/roleDefinitions/81a9662b-bebf-436f-a333-f67b29880f12' + 'Storage Blob Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe' + 'Storage Blob Data Owner': '/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + 'Storage Blob Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' + 'Storage Blob Delegator': '/providers/Microsoft.Authorization/roleDefinitions/db58b8e5-c6ad-4a2a-8342-4190687cbf4a' + 'Storage File Data SMB Share Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb' + 'Storage File Data SMB Share Elevated Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a7264617-510b-434b-a828-9731dc254ea7' + 'Storage File Data SMB Share Reader': '/providers/Microsoft.Authorization/roleDefinitions/aba4ae5f-2193-4029-9191-0cb91df5e314' + 'Storage Queue Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/974c5e8b-45b9-4653-ba55-5f855dd0fb88' + 'Storage Queue Data Message Processor': '/providers/Microsoft.Authorization/roleDefinitions/8a0f0c08-91a1-4084-bc3d-661d67233fed' + 'Storage Queue Data Message Sender': '/providers/Microsoft.Authorization/roleDefinitions/c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' + 'Storage Queue Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/19e7f393-937e-4f77-808e-94535e297925' + 'Storage Table Data Contributor': '/providers/Microsoft.Authorization/roleDefinitions/0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3' + 'Storage Table Data Reader': '/providers/Microsoft.Authorization/roleDefinitions/76199698-9eea-4c19-bc75-cec21354c6b6' + 'Stream Analytics Query Tester': '/providers/Microsoft.Authorization/roleDefinitions/1ec5b3c1-b17e-4e25-8312-2acb3c3c5abf' + 'Support Request Contributor': '/providers/Microsoft.Authorization/roleDefinitions/cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e' + 'Tag Contributor': '/providers/Microsoft.Authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f' + 'Template Spec Contributor': '/providers/Microsoft.Authorization/roleDefinitions/1c9b6475-caf0-4164-b5a1-2142a7116f4b' + 'Template Spec Reader': '/providers/Microsoft.Authorization/roleDefinitions/392ae280-861d-42bd-9ea5-08ee6d83b80e' + 'Test Base Reader': '/providers/Microsoft.Authorization/roleDefinitions/15e0f5a1-3450-4248-8e25-e2afe88a9e85' + 'Traffic Manager Contributor': '/providers/Microsoft.Authorization/roleDefinitions/a4b10055-b0c7-44c2-b00f-c7b5b3550cf7' + 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + 'Video Indexer Restricted Viewer': '/providers/Microsoft.Authorization/roleDefinitions/a2c4a527-7dc0-4ee3-897b-403ade70fafb' + 'Virtual Machine Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4' + 'Virtual Machine Contributor': '/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c' + 'Virtual Machine Local User Login': '/providers/Microsoft.Authorization/roleDefinitions/602da2ba-a5c2-41da-b01d-5360126ab525' + 'Virtual Machine User Login': '/providers/Microsoft.Authorization/roleDefinitions/fb879df8-f326-4884-b1cf-06f3ad86be52' + 'VM Scanner Operator': '/providers/Microsoft.Authorization/roleDefinitions/d24ecba3-c1f4-40fa-a7bb-4588a071e8fd' + 'Web Plan Contributor': '/providers/Microsoft.Authorization/roleDefinitions/2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b' + 'Web PubSub Service Owner (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/12cf5a90-567b-43ae-8102-96cf46c7d9b4' + 'Web PubSub Service Reader (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf' + 'Website Contributor': '/providers/Microsoft.Authorization/roleDefinitions/de139f84-1756-47ae-9be6-808fbbe84772' + 'Windows Admin Center Administrator Login': '/providers/Microsoft.Authorization/roleDefinitions/a6333a3e-0164-44c3-b281-7a577aff287f' + 'Workbook Contributor': '/providers/Microsoft.Authorization/roleDefinitions/e8ddcd69-c73f-4f9f-9844-4100522f16ad' + 'Workbook Reader': '/providers/Microsoft.Authorization/roleDefinitions/b279062a-9be3-42a0-92ae-8b3cf002ec4d' + 'WorkloadBuilder Migration Agent Role': '/providers/Microsoft.Authorization/roleDefinitions/d17ce0a2-0697-43bc-aac5-9113337ab61c' +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +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 scope this Role Assignment applies to.') +output scope string = subscription().id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/readme.md new file mode 100644 index 000000000..775643953 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/readme.md @@ -0,0 +1,51 @@ +# Role Assignment on Subscription level `[Microsoft.Authorization/roleAssignments/subscription]` + +With this module you can perform role assignments on a subscription level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `principalId` | string | The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity). | +| `roleDefinitionIdOrName` | string | You can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `condition` | string | `''` | | The conditions on the role assignment. This limits the resources it can be assigned to. | +| `conditionVersion` | string | `'2.0'` | `[2.0]` | Version of the condition. Currently accepted value is "2.0". | +| `delegatedManagedIdentityResourceId` | string | `''` | | ID of the delegated managed identity resource. | +| `description` | string | `''` | | The description of the role assignment. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `principalType` | string | `''` | `['', Device, ForeignGroup, Group, ServicePrincipal, User]` | The principal type of the assigned principal ID. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | | Subscription ID of the subscription to assign the RBAC role to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | 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_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleAssignments/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.common/deploy.test.bicep new file mode 100644 index 000000000..4f3ed6351 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.common/deploy.test.bicep @@ -0,0 +1,36 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'ardmgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + roleName: '<>-testRole-${serviceShort}' + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + managementGroup().id + ] + description: 'Test Custom Role Definition Standard (management group scope)' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.min/deploy.test.bicep new file mode 100644 index 000000000..bbef62514 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/mg.min/deploy.test.bicep @@ -0,0 +1,27 @@ +targetScope = 'managementGroup' + +// ========== // +// 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 = 'ardmgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../managementGroup/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + roleName: '<>-testRole-${serviceShort}' + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.common/deploy.test.bicep new file mode 100644 index 000000000..6dffa19b6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.common/deploy.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roledefinitions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ardrgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + roleName: '<>-testRole-${serviceShort}' + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + resourceGroup.id + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (resource group scope)' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.min/deploy.test.bicep new file mode 100644 index 000000000..4fc0b37ee --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/rg.min/deploy.test.bicep @@ -0,0 +1,46 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.authorization.roledefinitions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ardrgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../resourceGroup/deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + roleName: '<>-testRole-${serviceShort}' + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.common/deploy.test.bicep new file mode 100644 index 000000000..26fe4f564 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.common/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'ardsubcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + roleName: '<>-testRole-${serviceShort}' + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + subscription().id + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (subscription scope)' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.min/deploy.test.bicep new file mode 100644 index 000000000..472912c66 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/.test/sub.min/deploy.test.bicep @@ -0,0 +1,28 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'ardsubmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../subscription/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + roleName: '<>-testRole-${serviceShort}' + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + subscriptionId: subscription().subscriptionId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/deploy.bicep new file mode 100644 index 000000000..e2a523334 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/deploy.bicep @@ -0,0 +1,110 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param dataActions array = [] + +@sys.description('Optional. List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param notDataActions array = [] + +@sys.description('Optional. The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. The subscription ID where the Role Definition and Target Scope will be applied to. Use for both Subscription level and Resource Group Level.') +param subscriptionId string = '' + +@sys.description('Optional. The name of the Resource Group where the Role Definition and Target Scope will be applied to.') +param resourceGroupName string = '' + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module roleDefinition_mg 'managementGroup/deploy.bicep' = if (empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleDefinition-MG-Module' + scope: managementGroup(managementGroupId) + params: { + roleName: roleName + description: !empty(description) ? description : '' + actions: !empty(actions) ? actions : [] + notActions: !empty(notActions) ? notActions : [] + assignableScopes: !empty(assignableScopes) ? assignableScopes : [] + managementGroupId: managementGroupId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleDefinition_sub 'subscription/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${uniqueString(deployment().name, location)}-RoleDefinition-Sub-Module' + scope: subscription(subscriptionId) + params: { + roleName: roleName + description: !empty(description) ? description : '' + actions: !empty(actions) ? actions : [] + notActions: !empty(notActions) ? notActions : [] + dataActions: !empty(dataActions) ? dataActions : [] + notDataActions: !empty(notDataActions) ? notDataActions : [] + assignableScopes: !empty(assignableScopes) ? assignableScopes : [] + subscriptionId: subscriptionId + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module roleDefinition_rg 'resourceGroup/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${uniqueString(deployment().name, location)}-RoleDefinition-RG-Module' + scope: resourceGroup(subscriptionId, resourceGroupName) + params: { + roleName: roleName + description: !empty(description) ? description : '' + actions: !empty(actions) ? actions : [] + notActions: !empty(notActions) ? notActions : [] + dataActions: !empty(dataActions) ? dataActions : [] + notDataActions: !empty(notDataActions) ? notDataActions : [] + assignableScopes: !empty(assignableScopes) ? assignableScopes : [] + subscriptionId: subscriptionId + resourceGroupName: resourceGroupName + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_mg.outputs.name : (!empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_sub.outputs.name : roleDefinition_rg.outputs.name) + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_mg.outputs.resourceId : (!empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_sub.outputs.resourceId : roleDefinition_rg.outputs.resourceId) + +@sys.description('The scope this Role Definition applies to.') +output scope string = empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_mg.outputs.scope : (!empty(subscriptionId) && empty(resourceGroupName) ? roleDefinition_sub.outputs.scope : roleDefinition_rg.outputs.scope) diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep new file mode 100644 index 000000000..9e9b0ac70 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/deploy.bicep @@ -0,0 +1,63 @@ +targetScope = 'managementGroup' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param managementGroupId string = managementGroup().name + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' = { + name: guid(roleName, managementGroupId) + properties: { + roleName: roleName + description: description + type: 'customRole' + permissions: [ + { + actions: actions + notActions: notActions + } + ] + assignableScopes: assignableScopes == [] ? array(tenantResourceId('Microsoft.Management/managementGroups', managementGroupId)) : assignableScopes + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = roleDefinition.name + +@sys.description('The scope this Role Definition applies to.') +output scope string = managementGroup().id + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = roleDefinition.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md new file mode 100644 index 000000000..70ca254c4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/readme.md @@ -0,0 +1,49 @@ +# Role Definitions on Management Group level `[Microsoft.Authorization/roleDefinitions/managementGroup]` + +With this module you can create role definitions on a management group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | +| `notActions` | array | `[]` | List of denied actions. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/managementGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/readme.md new file mode 100644 index 000000000..83aade4a7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/readme.md @@ -0,0 +1,622 @@ +# Role Definitions `[Microsoft.Authorization/roleDefinitions]` + +This module deploys custom RBAC Role Definitions across the management group, subscription or resource group scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Module Usage Guidance](#Module-Usage-Guidance) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `dataActions` | array | `[]` | List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `managementGroupId` | string | `[managementGroup().name]` | The group ID of the Management Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | +| `notActions` | array | `[]` | List of denied actions. | +| `notDataActions` | array | `[]` | List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `resourceGroupName` | string | `''` | The name of the Resource Group where the Role Definition and Target Scope will be applied to. | +| `subscriptionId` | string | `''` | The subscription ID where the Role Definition and Target Scope will be applied to. Use for both Subscription level and Resource Group Level. | + + +### Parameter Usage: `managementGroupId` + +To deploy resource to a Management Group, provide the `managementGroupId` as an input parameter to the module. + +

+ +Parameter JSON format + +```json +"managementGroupId": { + "value": "contoso-group" +} +``` + +
+ + +
+ +Bicep format + +```bicep +managementGroupId: 'contoso-group' +``` + +
+

+ +> `managementGroupId` is an optional parameter. If not provided, the deployment will use the management group defined in the current deployment scope (i.e. `managementGroup().name`). + +### Parameter Usage: `subscriptionId` + +To deploy resource to an Azure Subscription, provide the `subscriptionId` as an input parameter to the module. **Example**: + + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +} +``` + +
+ +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +``` + +
+

+ +### Parameter Usage: `resourceGroupName` + +To deploy resource to a Resource Group, provide the `subscriptionId` and `resourceGroupName` as an input parameter to the module. **Example**: + +

+ +Parameter JSON format + +```json +"subscriptionId": { + "value": "12345678-b049-471c-95af-123456789012" +}, +"resourceGroupName": { + "value": "target-resourceGroup" +} +``` + +
+ + +
+ +Bicep format + +```bicep +subscriptionId: '12345678-b049-471c-95af-123456789012' +resourceGroupName: 'target-resourceGroup' +``` + +
+

+ +> The `subscriptionId` is used to enable deployment to a Resource Group Scope, allowing the use of the `resourceGroup()` function from a Management Group Scope. [Additional Details](https://github.com/Azure/bicep/pull/1420). + +## Module Usage Guidance + +In general, most of the resources under the `Microsoft.Authorization` namespace allows deploying resources at multiple scopes (management groups, subscriptions, resource groups). The `deploy.bicep` root module is simply an orchestrator module that targets sub-modules for different scopes as seen in the parameter usage section. All sub-modules for this namespace have folders that represent the target scope. For example, if the orchestrator module in the [root](deploy.bicep) needs to target 'subscription' level scopes. It will look at the relative path ['/subscription/deploy.bicep'](./subscription/deploy.bicep) and use this sub-module for the actual deployment, while still passing the same parameters from the root module. + +The above method is useful when you want to use a single point to interact with the module but rely on parameter combinations to achieve the target scope. But what if you want to incorporate this module in other modules with lower scopes? This would force you to deploy the module in scope `managementGroup` regardless and further require you to provide its ID with it. If you do not set the scope to management group, this would be the error that you can expect to face: + +```bicep +Error BCP134: Scope "subscription" is not valid for this module. Permitted scopes: "managementGroup" +``` + +The solution is to have the option of directly targeting the sub-module that achieves the required scope. For example, if you have your own Bicep file wanting to create resources at the subscription level, and also use some of the modules from the `Microsoft.Authorization` namespace, then you can directly use the sub-module ['/subscription/deploy.bicep'](./subscription/deploy.bicep) as a path within your repository, or reference that same published module from the bicep registry. CARML also published the sub-modules so you would be able to reference it like the following: + +**Bicep Registry Reference** +```bicep +module roledefinition 'br:bicepregistry.azurecr.io/bicep/modules/microsoft.authorization.roledefinitions.subscription:version' = {} +``` +**Local Path Reference** +```bicep +module roledefinition 'yourpath/modules/Microsoft.Authorization.roleDefinitions/subscription/deploy.bicep' = {} +``` + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Considerations + +This module can be deployed both at subscription or resource group level: + +- To deploy the module at resource group level, provide a valid name of an existing Resource Group in the `resourceGroupName` parameter and an existing subscription ID in the `subscriptionId` parameter. +- To deploy the module at the subscription level, provide an existing subscription ID in the `subscriptionId` parameter. +- To deploy the module at the management group level, provide an existing management group ID in the `managementGroupId` parameter. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Mg.Common

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-ardmgcom' + params: { + // Required parameters + roleName: '<>-testRole-ardmgcom' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + '' + ] + description: 'Test Custom Role Definition Standard (management group scope)' + enableDefaultTelemetry: '' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-testRole-ardmgcom" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "assignableScopes": { + "value": [ + "" + ] + }, + "description": { + "value": "Test Custom Role Definition Standard (management group scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/delete", + "Microsoft.Compute/images/write", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + } + } +} +``` + +
+

+ +

Example 2: Mg.Min

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-ardmgmin' + params: { + // Required parameters + roleName: '<>-testRole-ardmgmin' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-testRole-ardmgmin" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/images/read", + "Microsoft.Compute/galleries/read" + ] + }, + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 3: Rg.Common

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-ardrgcom' + params: { + // Required parameters + roleName: '<>-testRole-ardrgcom' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + '' + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (resource group scope)' + enableDefaultTelemetry: '' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-testRole-ardrgcom" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "assignableScopes": { + "value": [ + "" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "description": { + "value": "Test Custom Role Definition Standard (resource group scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/delete", + "Microsoft.Compute/images/write", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + } + } +} +``` + +
+

+ +

Example 4: Rg.Min

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-ardrgmin' + params: { + // Required parameters + roleName: '<>-testRole-ardrgmin' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-testRole-ardrgmin" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/images/read", + "Microsoft.Compute/galleries/read" + ] + }, + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 5: Sub.Common

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-ardsubcom' + params: { + // Required parameters + roleName: '<>-testRole-ardsubcom' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/*' + 'Microsoft.Network/virtualNetworks/read' + ] + assignableScopes: [ + '' + ] + dataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/*/read' + ] + description: 'Test Custom Role Definition Standard (subscription scope)' + enableDefaultTelemetry: '' + notActions: [ + 'Microsoft.Compute/images/delete' + 'Microsoft.Compute/images/write' + 'Microsoft.Network/virtualNetworks/subnets/join/action' + ] + notDataActions: [ + 'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read' + ] + } +} +``` + +
+

+ +

+ +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 + "roleName": { + "value": "<>-testRole-ardsubcom" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/*", + "Microsoft.Network/virtualNetworks/read" + ] + }, + "assignableScopes": { + "value": [ + "" + ] + }, + "dataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/*/read" + ] + }, + "description": { + "value": "Test Custom Role Definition Standard (subscription scope)" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "notActions": { + "value": [ + "Microsoft.Compute/images/delete", + "Microsoft.Compute/images/write", + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + }, + "notDataActions": { + "value": [ + "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read" + ] + } + } +} +``` + +
+

+ +

Example 6: Sub.Min

+ +
+ +via Bicep module + +```bicep +module roleDefinitions './Microsoft.Authorization/roleDefinitions/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-ardsubmin' + params: { + // Required parameters + roleName: '<>-testRole-ardsubmin' + // Non-required parameters + actions: [ + 'Microsoft.Compute/galleries/images/read' + 'Microsoft.Compute/galleries/read' + ] + enableDefaultTelemetry: '' + 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 + "roleName": { + "value": "<>-testRole-ardsubmin" + }, + // Non-required parameters + "actions": { + "value": [ + "Microsoft.Compute/galleries/images/read", + "Microsoft.Compute/galleries/read" + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "subscriptionId": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep new file mode 100644 index 000000000..d8014d87a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/deploy.bicep @@ -0,0 +1,73 @@ +targetScope = 'resourceGroup' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param dataActions array = [] + +@sys.description('Optional. List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param notDataActions array = [] + +@sys.description('Optional. The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. The name of the Resource Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param resourceGroupName string = resourceGroup().name + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' = { + name: guid(roleName, subscriptionId, resourceGroupName) + properties: { + roleName: roleName + description: description + type: 'customRole' + permissions: [ + { + actions: actions + notActions: notActions + dataActions: dataActions + notDataActions: notDataActions + } + ] + assignableScopes: assignableScopes == [] ? array(resourceGroup().id) : assignableScopes + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = roleDefinition.name + +@sys.description('The scope this Role Definition applies to.') +output scope string = resourceGroup().id + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = roleDefinition.id + +@sys.description('The name of the resource group the role definition was created at.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md new file mode 100644 index 000000000..617c48494 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/readme.md @@ -0,0 +1,52 @@ +# Role Definitions on Resource Group level `[Microsoft.Authorization/roleDefinitions/resourceGroup]` + +With this module you can create role definitions on a resource group level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `dataActions` | array | `[]` | List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `notActions` | array | `[]` | List of denied actions. | +| `notDataActions` | array | `[]` | List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `resourceGroupName` | string | `[resourceGroup().name]` | The name of the Resource Group where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceGroupName` | string | The name of the resource group the role definition was created at. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/resourceGroup/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep new file mode 100644 index 000000000..3f142bd89 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/deploy.bicep @@ -0,0 +1,71 @@ +targetScope = 'subscription' + +@sys.description('Required. Name of the custom RBAC role to be created.') +param roleName string + +@sys.description('Optional. Description of the custom RBAC role to be created.') +param description string = '' + +@sys.description('Optional. List of allowed actions.') +param actions array = [] + +@sys.description('Optional. List of denied actions.') +param notActions array = [] + +@sys.description('Optional. List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param dataActions array = [] + +@sys.description('Optional. List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes.') +param notDataActions array = [] + +@sys.description('Optional. The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment.') +param subscriptionId string = subscription().subscriptionId + +@sys.description('Optional. Role definition assignable scopes. If not provided, will use the current scope provided.') +param assignableScopes array = [] + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' = { + name: guid(roleName, subscriptionId) + properties: { + roleName: roleName + description: description + type: 'customRole' + permissions: [ + { + actions: actions + notActions: notActions + dataActions: dataActions + notDataActions: notDataActions + } + ] + assignableScopes: !empty(assignableScopes) ? assignableScopes : array(subscription().id) + } +} + +@sys.description('The GUID of the Role Definition.') +output name string = roleDefinition.name + +@sys.description('The scope this Role Definition applies to.') +output scope string = subscription().id + +@sys.description('The resource ID of the Role Definition.') +output resourceId string = roleDefinition.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/readme.md new file mode 100644 index 000000000..95d8dd4d2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/readme.md @@ -0,0 +1,51 @@ +# Role Definitions on Subscription level `[Microsoft.Authorization/roleDefinitions/subscription]` + +With this module you can create role definitions on a subscription level + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/roleDefinitions` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleDefinitions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `roleName` | string | Name of the custom RBAC role to be created. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | List of allowed actions. | +| `assignableScopes` | array | `[]` | Role definition assignable scopes. If not provided, will use the current scope provided. | +| `dataActions` | array | `[]` | List of allowed data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `description` | string | `''` | Description of the custom RBAC role to be created. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `notActions` | array | `[]` | List of denied actions. | +| `notDataActions` | array | `[]` | List of denied data actions. This is not supported if the assignableScopes contains Management Group Scopes. | +| `subscriptionId` | string | `[subscription().subscriptionId]` | The subscription ID where the Role Definition and Target Scope will be applied to. If not provided, will use the current scope for deployment. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The GUID of the Role Definition. | +| `resourceId` | string | The resource ID of the Role Definition. | +| `scope` | string | The scope this Role Definition applies to. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/subscription/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Authorization/roleDefinitions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..575f471ef --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,72 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Resource Policy Contributor': subscriptionResourceId('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') +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2020-01-13-preview' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(automationAccount.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: automationAccount +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/dependencies.bicep new file mode 100644 index 000000000..c9e11ef44 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/dependencies.bicep @@ -0,0 +1,90 @@ +@description('Optional. The location to deploy resources 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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.azure-automation.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +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 +} + +@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 Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The URL of the created Key Vault.') +output keyVaultUrl string = keyVault.properties.vaultUri + +@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 resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/deploy.test.bicep new file mode 100644 index 000000000..31af607da --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/common/deploy.test.bicep @@ -0,0 +1,244 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.automation.account-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'aacom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + keyVaultName: 'dep-<>-kv-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + gallerySolutions: [ + { + name: 'Updates' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + jobSchedules: [ + { + runbookName: 'TestRunbook' + scheduleName: 'TestSchedule' + } + ] + disableLocalAuth: true + linkedWorkspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + lock: 'CanNotDelete' + modules: [ + { + name: 'PSWindowsUpdate' + uri: 'https://www.powershellgallery.com/api/v2/package' + version: 'latest' + } + ] + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } + service: 'Webhook' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } + service: 'DSCAndHybridWorker' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + runbooks: [ + { + description: 'Test runbook' + name: 'TestRunbook' + type: 'PowerShell' + uri: 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1' + version: '1.0.0.0' + } + ] + schedules: [ + { + advancedSchedule: {} + expiryTime: '9999-12-31T13:00' + frequency: 'Hour' + interval: 12 + name: 'TestSchedule' + startTime: '' + timeZone: 'Europe/Berlin' + } + ] + softwareUpdateConfigurations: [ + { + excludeUpdates: [ + '123456' + ] + frequency: 'Month' + includeUpdates: [ + '654321' + ] + interval: 1 + maintenanceWindow: 'PT4H' + monthlyOccurrences: [ + { + day: 'Friday' + occurrence: 3 + } + ] + name: 'Windows_ZeroDay' + operatingSystem: 'Windows' + rebootSetting: 'IfRequired' + scopeByTags: { + Update: [ + 'Automatic-Wave1' + ] + } + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Definition' + 'FeaturePack' + 'Security' + 'ServicePack' + 'Tools' + 'UpdateRollup' + 'Updates' + ] + } + { + excludeUpdates: [ + 'icacls' + ] + frequency: 'OneTime' + includeUpdates: [ + 'kernel' + ] + maintenanceWindow: 'PT4H' + name: 'Linux_ZeroDay' + operatingSystem: 'Linux' + rebootSetting: 'IfRequired' + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Other' + 'Security' + ] + } + ] + systemAssignedIdentity: true + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + variables: [ + { + description: 'TestStringDescription' + name: 'TestString' + value: '\'TestString\'' + } + { + description: 'TestIntegerDescription' + name: 'TestInteger' + value: '500' + } + { + description: 'TestBooleanDescription' + name: 'TestBoolean' + value: 'false' + } + { + description: 'TestDateTimeDescription' + isEncrypted: false + name: 'TestDateTime' + value: '\'\\/Date(1637934042656)\\/\'' + } + { + description: 'TestEncryptedDescription' + name: 'TestEncryptedVariable' + value: '\'TestEncryptedValue\'' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/dependencies.bicep new file mode 100644 index 000000000..c0fbbed61 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/dependencies.bicep @@ -0,0 +1,58 @@ +@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 + softDeleteRetentionInDays: 7 + enablePurgeProtection: true + 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', '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 Key Vault Encryption Key.') +output keyVaultEncryptionKeyName string = keyVault::key.name + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/deploy.test.bicep new file mode 100644 index 000000000..a1474d932 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/encr/deploy.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.automation.account-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'aaencr' + +@description('Generated. Used as a basis for unique resource names.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + // 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-<>-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + cMKKeyName: nestedDependencies.outputs.keyVaultEncryptionKeyName + cMKKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + cMKUserAssignedIdentityResourceId: nestedDependencies.outputs.managedIdentityResourceId + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/min/deploy.test.bicep new file mode 100644 index 000000000..db0b76eab --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.automation.account-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'aamin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/deploy.bicep new file mode 100644 index 000000000..59bb6a502 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/deploy.bicep @@ -0,0 +1,422 @@ +@description('Required. Name of the Automation Account.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. SKU name of the account.') +@allowed([ + 'Free' + 'Basic' +]) +param skuName string = 'Basic' + +@description('Conditional. The resource ID of a key vault to reference a customer managed key for encryption from. Required if \'cMKKeyName\' is not empty.') +param cMKKeyVaultResourceId string = '' + +@description('Optional. The name of the customer managed key to use for encryption.') +param cMKKeyName string = '' + +@description('Conditional. User assigned identity to use when fetching the customer managed key. Required if \'cMKKeyName\' is not empty.') +param cMKUserAssignedIdentityResourceId string = '' + +@description('Optional. The version of the customer managed key to reference for encryption. If not provided, the latest key version is used.') +param cMKKeyVersion string = '' + +@description('Optional. List of modules to be created in the automation account.') +param modules array = [] + +@description('Optional. List of runbooks to be created in the automation account.') +param runbooks array = [] + +@description('Optional. List of schedules to be created in the automation account.') +param schedules array = [] + +@description('Optional. List of jobSchedules to be created in the automation account.') +param jobSchedules array = [] + +@description('Optional. List of variables to be created in the automation account.') +param variables array = [] + +@description('Optional. ID of the log analytics workspace to be linked to the deployed automation account.') +param linkedWorkspaceResourceId string = '' + +@description('Optional. List of gallerySolutions to be created in the linked log analytics workspace.') +param gallerySolutions array = [] + +@description('Optional. List of softwareUpdateConfigurations to be created in the automation account.') +param softwareUpdateConfigurations array = [] + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@description('Optional. Disable local authentication profile used within the resource.') +param disableLocalAuth bool = true + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@minValue(0) +@maxValue(365) +@description('Optional. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +param diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the Automation Account resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'JobLogs' + 'JobStreams' + 'DscNodeStatus' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true +}] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource cMKKeyVault 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId)) { + name: last(split(cMKKeyVaultResourceId, '/'))! + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource cMKKeyVaultKey 'Microsoft.KeyVault/vaults/keys@2021-10-01' existing = if (!empty(cMKKeyVaultResourceId) && !empty(cMKKeyName)) { + name: '${last(split(cMKKeyVaultResourceId, '/'))}/${cMKKeyName}'! + scope: resourceGroup(split(cMKKeyVaultResourceId, '/')[2], split(cMKKeyVaultResourceId, '/')[4]) +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = { + name: name + location: location + tags: tags + identity: identity + properties: { + sku: { + name: skuName + } + encryption: !empty(cMKKeyName) ? { + keySource: 'Microsoft.KeyVault' + identity: { + userAssignedIdentity: cMKUserAssignedIdentityResourceId + } + keyVaultProperties: { + keyName: cMKKeyName + keyVaultUri: cMKKeyVault.properties.vaultUri + keyVersion: !empty(cMKKeyVersion) ? cMKKeyVersion : last(split(cMKKeyVaultKey.properties.keyUriWithVersion, '/')) + } + } : null + publicNetworkAccess: !empty(publicNetworkAccess) ? (publicNetworkAccess == 'Disabled' ? false : true) : (!empty(privateEndpoints) ? false : null) + disableLocalAuth: disableLocalAuth + } +} + +module automationAccount_modules 'modules/deploy.bicep' = [for (module, index) in modules: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Module-${index}' + params: { + name: module.name + automationAccountName: automationAccount.name + version: module.version + uri: module.uri + location: location + tags: tags + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_schedules 'schedules/deploy.bicep' = [for (schedule, index) in schedules: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Schedule-${index}' + params: { + name: schedule.name + automationAccountName: automationAccount.name + advancedSchedule: contains(schedule, 'advancedSchedule') ? schedule.advancedSchedule : null + description: contains(schedule, 'description') ? schedule.description : '' + expiryTime: contains(schedule, 'expiryTime') ? schedule.expiryTime : '' + frequency: contains(schedule, 'frequency') ? schedule.frequency : 'OneTime' + interval: contains(schedule, 'interval') ? schedule.interval : 0 + startTime: contains(schedule, 'startTime') ? schedule.startTime : '' + timeZone: contains(schedule, 'timeZone') ? schedule.timeZone : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_runbooks 'runbooks/deploy.bicep' = [for (runbook, index) in runbooks: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Runbook-${index}' + params: { + name: runbook.name + automationAccountName: automationAccount.name + type: runbook.type + description: contains(runbook, 'description') ? runbook.description : '' + uri: contains(runbook, 'uri') ? runbook.uri : '' + version: contains(runbook, 'version') ? runbook.version : '' + location: location + tags: tags + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_jobSchedules 'jobSchedules/deploy.bicep' = [for (jobSchedule, index) in jobSchedules: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-JobSchedule-${index}' + params: { + automationAccountName: automationAccount.name + runbookName: jobSchedule.runbookName + scheduleName: jobSchedule.scheduleName + parameters: contains(jobSchedule, 'parameters') ? jobSchedule.parameters : {} + runOn: contains(jobSchedule, 'runOn') ? jobSchedule.runOn : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + automationAccount_schedules + automationAccount_runbooks + ] +}] + +module automationAccount_variables 'variables/deploy.bicep' = [for (variable, index) in variables: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Variable-${index}' + params: { + automationAccountName: automationAccount.name + name: variable.name + description: contains(variable, 'description') ? variable.description : '' + value: variable.value + isEncrypted: contains(variable, 'isEncrypted') ? variable.isEncrypted : true + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module automationAccount_linkedService '../../Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep' = if (!empty(linkedWorkspaceResourceId)) { + name: '${uniqueString(deployment().name, location)}-AutoAccount-LinkedService' + params: { + name: 'automation' + logAnalyticsWorkspaceName: last(split(linkedWorkspaceResourceId, '/'))! + enableDefaultTelemetry: enableReferencedModulesTelemetry + resourceId: automationAccount.id + tags: tags + } + // This is to support linked services to law in different subscription and resource group than the automation account. + // The current scope is used by default if no linked service is intended to be created. + scope: resourceGroup(!empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[2] : subscription().subscriptionId, !empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[4] : resourceGroup().name) +} + +module automationAccount_solutions '../../Microsoft.OperationsManagement/solutions/deploy.bicep' = [for (gallerySolution, index) in gallerySolutions: if (!empty(linkedWorkspaceResourceId)) { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Solution-${index}' + params: { + name: gallerySolution.name + location: location + logAnalyticsWorkspaceName: last(split(linkedWorkspaceResourceId, '/'))! + product: contains(gallerySolution, 'product') ? gallerySolution.product : 'OMSGallery' + publisher: contains(gallerySolution, 'publisher') ? gallerySolution.publisher : 'Microsoft' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + // This is to support solution to law in different subscription and resource group than the automation account. + // The current scope is used by default if no linked service is intended to be created. + scope: resourceGroup(!empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[2] : subscription().subscriptionId, !empty(linkedWorkspaceResourceId) ? split(linkedWorkspaceResourceId, '/')[4] : resourceGroup().name) + dependsOn: [ + automationAccount_linkedService + ] +}] + +module automationAccount_softwareUpdateConfigurations 'softwareUpdateConfigurations/deploy.bicep' = [for (softwareUpdateConfiguration, index) in softwareUpdateConfigurations: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-SwUpdateConfig-${index}' + params: { + name: softwareUpdateConfiguration.name + automationAccountName: automationAccount.name + frequency: softwareUpdateConfiguration.frequency + operatingSystem: softwareUpdateConfiguration.operatingSystem + rebootSetting: softwareUpdateConfiguration.rebootSetting + azureVirtualMachines: contains(softwareUpdateConfiguration, 'azureVirtualMachines') ? softwareUpdateConfiguration.azureVirtualMachines : [] + excludeUpdates: contains(softwareUpdateConfiguration, 'excludeUpdates') ? softwareUpdateConfiguration.excludeUpdates : [] + expiryTime: contains(softwareUpdateConfiguration, 'expiryTime') ? softwareUpdateConfiguration.expiryTime : '' + expiryTimeOffsetMinutes: contains(softwareUpdateConfiguration, 'expiryTimeOffsetMinutes') ? softwareUpdateConfiguration.expiryTimeOffsetMinute : 0 + includeUpdates: contains(softwareUpdateConfiguration, 'includeUpdates') ? softwareUpdateConfiguration.includeUpdates : [] + interval: contains(softwareUpdateConfiguration, 'interval') ? softwareUpdateConfiguration.interval : 1 + isEnabled: contains(softwareUpdateConfiguration, 'isEnabled') ? softwareUpdateConfiguration.isEnabled : true + maintenanceWindow: contains(softwareUpdateConfiguration, 'maintenanceWindow') ? softwareUpdateConfiguration.maintenanceWindow : 'PT2H' + monthDays: contains(softwareUpdateConfiguration, 'monthDays') ? softwareUpdateConfiguration.monthDays : [] + monthlyOccurrences: contains(softwareUpdateConfiguration, 'monthlyOccurrences') ? softwareUpdateConfiguration.monthlyOccurrences : [] + nextRun: contains(softwareUpdateConfiguration, 'nextRun') ? softwareUpdateConfiguration.nextRun : '' + nextRunOffsetMinutes: contains(softwareUpdateConfiguration, 'nextRunOffsetMinutes') ? softwareUpdateConfiguration.nextRunOffsetMinutes : 0 + nonAzureComputerNames: contains(softwareUpdateConfiguration, 'nonAzureComputerNames') ? softwareUpdateConfiguration.nonAzureComputerNames : [] + nonAzureQueries: contains(softwareUpdateConfiguration, 'nonAzureQueries') ? softwareUpdateConfiguration.nonAzureQueries : [] + postTaskParameters: contains(softwareUpdateConfiguration, 'postTaskParameters') ? softwareUpdateConfiguration.postTaskParameters : {} + postTaskSource: contains(softwareUpdateConfiguration, 'postTaskSource') ? softwareUpdateConfiguration.postTaskSource : '' + preTaskParameters: contains(softwareUpdateConfiguration, 'preTaskParameters') ? softwareUpdateConfiguration.preTaskParameters : {} + preTaskSource: contains(softwareUpdateConfiguration, 'preTaskSource') ? softwareUpdateConfiguration.preTaskSource : '' + scheduleDescription: contains(softwareUpdateConfiguration, 'scheduleDescription') ? softwareUpdateConfiguration.scheduleDescription : '' + scopeByLocations: contains(softwareUpdateConfiguration, 'scopeByLocations') ? softwareUpdateConfiguration.scopeByLocations : [] + scopeByResources: contains(softwareUpdateConfiguration, 'scopeByResources') ? softwareUpdateConfiguration.scopeByResources : [ + subscription().id + ] + scopeByTags: contains(softwareUpdateConfiguration, 'scopeByTags') ? softwareUpdateConfiguration.scopeByTags : {} + scopeByTagsOperation: contains(softwareUpdateConfiguration, 'scopeByTagsOperation') ? softwareUpdateConfiguration.scopeByTagsOperation : 'All' + startTime: contains(softwareUpdateConfiguration, 'startTime') ? softwareUpdateConfiguration.startTime : '' + timeZone: contains(softwareUpdateConfiguration, 'timeZone') ? softwareUpdateConfiguration.timeZone : 'UTC' + updateClassifications: contains(softwareUpdateConfiguration, 'updateClassifications') ? softwareUpdateConfiguration.updateClassifications : [ + 'Critical' + 'Security' + ] + weekDays: contains(softwareUpdateConfiguration, 'weekDays') ? softwareUpdateConfiguration.weekDays : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + automationAccount_solutions + ] +}] + +resource automationAccount_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${automationAccount.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: automationAccount +} + +resource automationAccount_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: automationAccount +} + +module automationAccount_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-AutomationAccount-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(automationAccount.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: automationAccount.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + ipConfigurations: contains(privateEndpoint, 'ipConfigurations') ? privateEndpoint.ipConfigurations : [] + applicationSecurityGroups: contains(privateEndpoint, 'applicationSecurityGroups') ? privateEndpoint.applicationSecurityGroups : [] + customNetworkInterfaceName: contains(privateEndpoint, 'customNetworkInterfaceName') ? privateEndpoint.customNetworkInterfaceName : '' + } +}] + +module automationAccount_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AutoAccount-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: automationAccount.id + } +}] + +@description('The name of the deployed automation account.') +output name string = automationAccount.name + +@description('The resource ID of the deployed automation account.') +output resourceId string = automationAccount.id + +@description('The resource group of the deployed automation account.') +output resourceGroupName string = resourceGroup().name + +@description('The principal ID of the system assigned identity.') +output systemAssignedPrincipalId string = systemAssignedIdentity && contains(automationAccount.identity, 'principalId') ? automationAccount.identity.principalId : '' + +@description('The location the resource was deployed into.') +output location string = automationAccount.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep new file mode 100644 index 000000000..dac058a11 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/deploy.bicep @@ -0,0 +1,62 @@ +@description('Generated. Name of the Automation Account job schedule. Must be a GUID and is autogenerated. No need to provide this value.') +param name string = newGuid() + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Required. The runbook property associated with the entity.') +param runbookName string + +@description('Required. The schedule property associated with the entity.') +param scheduleName string + +@description('Optional. List of job properties.') +param parameters object = {} + +@description('Optional. The hybrid worker group that the scheduled job should run on.') +param runOn string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { + name: automationAccountName +} + +resource jobSchedule 'Microsoft.Automation/automationAccounts/jobSchedules@2022-08-08' = { + // For each job schedule deployed with an ARM template, the GUID must be unique. Even if you're rescheduling an existing schedule, you'll need to change the GUID. This applies even if you've previously deleted an existing job schedule that was created with the same template. Reusing the same GUID results in a failed deployment. + #disable-next-line use-stable-resource-identifiers + name: name + parent: automationAccount + properties: { + parameters: parameters + runbook: { + name: runbookName + } + runOn: !empty(runOn) ? runOn : null + schedule: { + name: scheduleName + } + } +} + +@description('The name of the deployed job schedule.') +output name string = jobSchedule.name + +@description('The resource ID of the deployed job schedule.') +output resourceId string = jobSchedule.id + +@description('The resource group of the deployed job schedule.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/readme.md new file mode 100644 index 000000000..d5f88fc04 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/readme.md @@ -0,0 +1,58 @@ +# Automation Account Job Schedules `[Microsoft.Automation/automationAccounts/jobSchedules]` + +This module deploys an Azure Automation Account Job Schedule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/jobSchedules` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/jobSchedules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `runbookName` | string | The runbook property associated with the entity. | +| `scheduleName` | string | The schedule property associated with the entity. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `parameters` | object | `{object}` | List of job properties. | +| `runOn` | string | `''` | The hybrid worker group that the scheduled job should run on. | + +**Generated parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | `[newGuid()]` | Name of the Automation Account job schedule. Must be a GUID and is autogenerated. No need to provide this value. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed job schedule. | +| `resourceGroupName` | string | The resource group of the deployed job schedule. | +| `resourceId` | string | The resource ID of the deployed job schedule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/jobSchedules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/deploy.bicep new file mode 100644 index 000000000..05df955a4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/deploy.bicep @@ -0,0 +1,61 @@ +@description('Required. Name of the Automation Account module.') +param name string + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Required. Module package URI, e.g. https://www.powershellgallery.com/api/v2/package.') +param uri string + +@description('Optional. Module version or specify latest to get the latest version.') +param version string = 'latest' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Automation Account resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { + name: automationAccountName +} + +resource module 'Microsoft.Automation/automationAccounts/modules@2022-08-08' = { + name: name + parent: automationAccount + location: location + tags: tags + properties: { + contentLink: { + uri: version != 'latest' ? '${uri}/${name}/${version}' : '${uri}/${name}' + version: version != 'latest' ? version : null + } + } +} + +@description('The name of the deployed module.') +output name string = module.name + +@description('The resource ID of the deployed module.') +output resourceId string = module.id + +@description('The resource group of the deployed module.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = module.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/readme.md new file mode 100644 index 000000000..2c4020970 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/readme.md @@ -0,0 +1,95 @@ +# Automation Account Modules `[Microsoft.Automation/automationAccounts/modules]` + +This module deploys an Azure Automation Account Module. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/modules` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/modules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Automation Account module. | +| `uri` | string | Module package URI, e.g. https://www.powershellgallery.com/api/v2/package. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `tags` | object | `{object}` | Tags of the Automation Account resource. | +| `version` | string | `'latest'` | Module version or specify latest to get the latest version. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed module. | +| `resourceGroupName` | string | The resource group of the deployed module. | +| `resourceId` | string | The resource ID of the deployed module. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/modules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/readme.md new file mode 100644 index 000000000..e9b268ad2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/readme.md @@ -0,0 +1,946 @@ +# Automation Accounts `[Microsoft.Automation/automationAccounts]` + +This module deploys an Azure Automation Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Automation/automationAccounts` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts) | +| `Microsoft.Automation/automationAccounts/jobSchedules` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/jobSchedules) | +| `Microsoft.Automation/automationAccounts/modules` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/modules) | +| `Microsoft.Automation/automationAccounts/runbooks` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/runbooks) | +| `Microsoft.Automation/automationAccounts/schedules` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/schedules) | +| `Microsoft.Automation/automationAccounts/softwareUpdateConfigurations` | [2019-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/softwareUpdateConfigurations) | +| `Microsoft.Automation/automationAccounts/variables` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/variables) | +| `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` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Automation Account. | + +**Conditional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `cMKKeyVaultResourceId` | string | `''` | The resource ID of a key vault to reference a customer managed key for encryption from. Required if 'cMKKeyName' is not empty. | +| `cMKUserAssignedIdentityResourceId` | string | `''` | User assigned identity to use when fetching the customer managed key. Required if 'cMKKeyName' is not empty. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `cMKKeyName` | string | `''` | | The name of the customer managed key to use for encryption. | +| `cMKKeyVersion` | string | `''` | | The version of the customer managed key to reference for encryption. If not provided, the latest key version is used. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, DscNodeStatus, JobLogs, JobStreams]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableLocalAuth` | bool | `True` | | Disable local authentication profile used within the resource. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `gallerySolutions` | array | `[]` | | List of gallerySolutions to be created in the linked log analytics workspace. | +| `jobSchedules` | _[jobSchedules](jobSchedules/readme.md)_ array | `[]` | | List of jobSchedules to be created in the automation account. | +| `linkedWorkspaceResourceId` | string | `''` | | ID of the log analytics workspace to be linked to the deployed automation account. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `modules` | _[modules](modules/readme.md)_ array | `[]` | | List of modules to be created in the automation account. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `['', Disabled, Enabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `runbooks` | _[runbooks](runbooks/readme.md)_ array | `[]` | | List of runbooks to be created in the automation account. | +| `schedules` | _[schedules](schedules/readme.md)_ array | `[]` | | List of schedules to be created in the automation account. | +| `skuName` | string | `'Basic'` | `[Basic, Free]` | SKU name of the account. | +| `softwareUpdateConfigurations` | _[softwareUpdateConfigurations](softwareUpdateConfigurations/readme.md)_ array | `[]` | | List of softwareUpdateConfigurations to be created in the automation account. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Automation Account resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `variables` | _[variables](variables/readme.md)_ array | `[]` | | List of variables to be created in the automation account. | + + +### Parameter Usage: `encryption` + +Prerequisites: + +- User Assigned Identity for Encryption needs `Get`, `List`, `Wrap` and `Unwrap` permissions on the key. +- User Assigned Identity have to be one of the defined identities in userAssignedIdentities parameter block. +- To use Azure Automation with customer managed keys, both `Soft Delete` and `Do Not Purge` features must be turned on to allow for recovery of keys in case of accidental deletion. + +

+ +Parameter JSON format + +```json +"encryptionKeySource" : { + "value" : "Microsoft.KeyVault" +}, +"encryptionUserAssignedIdentity": { + "value": "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001" // this identity needs to be one of the identities defined in userAssignedIdentities section +}, +"keyName" : { + "value" : "keyEncryptionKey" +}, +"keyvaultUri" : { + "value" : "https://<>.vault.azure.net/" +}, +"keyVersion" : { + "value" : "aa11b22c1234567890c3608c657cd5a2" +}, +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001": {}, // same value as 'encryptionUserAssignedIdentity' parameter + } +} +``` + +
+ +
+ +Bicep format + +```bicep +encryptionKeySource: 'Microsoft.KeyVault' +encryptionUserAssignedIdentity: '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001' // this identity needs to be one of the identities defined in userAssignedIdentities section +keyName : 'keyEncryptionKey' +keyvaultUri: 'https://<>.vault.azure.net/' +keyVersion: 'aa11b22c1234567890c3608c657cd5a2' +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-<>-az-msi-x-001': {} // same value as 'encryptionUserAssignedIdentity' parameter +} +``` + +
+

+ +### Parameter Usage: `privateEndpoints` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +

+ +Parameter JSON format + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + }, + "ipConfigurations":[ + { + "name": "myIPconfigTest02", + "properties": { + "groupId": "blob", + "memberName": "blob", + "privateIPAddress": "10.0.0.30" + } + } + ], + "customDnsConfigs": [ + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // Example showing all available fields + { + name: 'sxx-az-pe' // Optional: Name will be automatically generated if one is not provided here + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + privateDnsZoneGroup: { + privateDNSResourceIds: [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + customDnsConfigs: [ + { + fqdn: 'customname.test.local' + ipAddresses: [ + '10.10.10.10' + ] + } + ] + ipConfigurations:[ + { + name: 'myIPconfigTest02' + properties: { + groupId: 'blob' + memberName: 'blob' + privateIPAddress: '10.0.0.30' + } + } + ] + } + // Example showing only mandatory fields + { + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed automation account. | +| `resourceGroupName` | string | The resource group of the deployed automation account. | +| `resourceId` | string | The resource ID of the deployed automation account. | +| `systemAssignedPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | Local reference | +| `Microsoft.OperationsManagement/solutions` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module automationAccounts './Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-aacom' + params: { + // Required parameters + name: '<>aacom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + disableLocalAuth: true + enableDefaultTelemetry: '' + gallerySolutions: [ + { + name: 'Updates' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + jobSchedules: [ + { + runbookName: 'TestRunbook' + scheduleName: 'TestSchedule' + } + ] + linkedWorkspaceResourceId: '' + lock: 'CanNotDelete' + modules: [ + { + name: 'PSWindowsUpdate' + uri: 'https://www.powershellgallery.com/api/v2/package' + version: 'latest' + } + ] + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '' + ] + } + service: 'Webhook' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '' + ] + } + service: 'DSCAndHybridWorker' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + runbooks: [ + { + description: 'Test runbook' + name: 'TestRunbook' + type: 'PowerShell' + uri: 'https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1' + version: '1.0.0.0' + } + ] + schedules: [ + { + advancedSchedule: {} + expiryTime: '9999-12-31T13:00' + frequency: 'Hour' + interval: 12 + name: 'TestSchedule' + startTime: '' + timeZone: 'Europe/Berlin' + } + ] + softwareUpdateConfigurations: [ + { + excludeUpdates: [ + '123456' + ] + frequency: 'Month' + includeUpdates: [ + '654321' + ] + interval: 1 + maintenanceWindow: 'PT4H' + monthlyOccurrences: [ + { + day: 'Friday' + occurrence: 3 + } + ] + name: 'Windows_ZeroDay' + operatingSystem: 'Windows' + rebootSetting: 'IfRequired' + scopeByTags: { + Update: [ + 'Automatic-Wave1' + ] + } + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Definition' + 'FeaturePack' + 'Security' + 'ServicePack' + 'Tools' + 'UpdateRollup' + 'Updates' + ] + } + { + excludeUpdates: [ + 'icacls' + ] + frequency: 'OneTime' + includeUpdates: [ + 'kernel' + ] + maintenanceWindow: 'PT4H' + name: 'Linux_ZeroDay' + operatingSystem: 'Linux' + rebootSetting: 'IfRequired' + startTime: '22:00' + updateClassifications: [ + 'Critical' + 'Other' + 'Security' + ] + } + ] + systemAssignedIdentity: true + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + userAssignedIdentities: { + '': {} + } + variables: [ + { + description: 'TestStringDescription' + name: 'TestString' + value: '\'TestString\'' + } + { + description: 'TestIntegerDescription' + name: 'TestInteger' + value: '500' + } + { + description: 'TestBooleanDescription' + name: 'TestBoolean' + value: 'false' + } + { + description: 'TestDateTimeDescription' + isEncrypted: false + name: 'TestDateTime' + value: '\'\\/Date(1637934042656)\\/\'' + } + { + description: 'TestEncryptedDescription' + name: 'TestEncryptedVariable' + value: '\'TestEncryptedValue\'' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>aacom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "disableLocalAuth": { + "value": true + }, + "enableDefaultTelemetry": { + "value": "" + }, + "gallerySolutions": { + "value": [ + { + "name": "Updates", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "jobSchedules": { + "value": [ + { + "runbookName": "TestRunbook", + "scheduleName": "TestSchedule" + } + ] + }, + "linkedWorkspaceResourceId": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "modules": { + "value": [ + { + "name": "PSWindowsUpdate", + "uri": "https://www.powershellgallery.com/api/v2/package", + "version": "latest" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "" + ] + }, + "service": "Webhook", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "" + ] + }, + "service": "DSCAndHybridWorker", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "runbooks": { + "value": [ + { + "description": "Test runbook", + "name": "TestRunbook", + "type": "PowerShell", + "uri": "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.automation/101-automation/scripts/AzureAutomationTutorial.ps1", + "version": "1.0.0.0" + } + ] + }, + "schedules": { + "value": [ + { + "advancedSchedule": {}, + "expiryTime": "9999-12-31T13:00", + "frequency": "Hour", + "interval": 12, + "name": "TestSchedule", + "startTime": "", + "timeZone": "Europe/Berlin" + } + ] + }, + "softwareUpdateConfigurations": { + "value": [ + { + "excludeUpdates": [ + "123456" + ], + "frequency": "Month", + "includeUpdates": [ + "654321" + ], + "interval": 1, + "maintenanceWindow": "PT4H", + "monthlyOccurrences": [ + { + "day": "Friday", + "occurrence": 3 + } + ], + "name": "Windows_ZeroDay", + "operatingSystem": "Windows", + "rebootSetting": "IfRequired", + "scopeByTags": { + "Update": [ + "Automatic-Wave1" + ] + }, + "startTime": "22:00", + "updateClassifications": [ + "Critical", + "Definition", + "FeaturePack", + "Security", + "ServicePack", + "Tools", + "UpdateRollup", + "Updates" + ] + }, + { + "excludeUpdates": [ + "icacls" + ], + "frequency": "OneTime", + "includeUpdates": [ + "kernel" + ], + "maintenanceWindow": "PT4H", + "name": "Linux_ZeroDay", + "operatingSystem": "Linux", + "rebootSetting": "IfRequired", + "startTime": "22:00", + "updateClassifications": [ + "Critical", + "Other", + "Security" + ] + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "userAssignedIdentities": { + "value": { + "": {} + } + }, + "variables": { + "value": [ + { + "description": "TestStringDescription", + "name": "TestString", + "value": "\"TestString\"" + }, + { + "description": "TestIntegerDescription", + "name": "TestInteger", + "value": "500" + }, + { + "description": "TestBooleanDescription", + "name": "TestBoolean", + "value": "false" + }, + { + "description": "TestDateTimeDescription", + "isEncrypted": false, + "name": "TestDateTime", + "value": "\"\\/Date(1637934042656)\\/\"" + }, + { + "description": "TestEncryptedDescription", + "name": "TestEncryptedVariable", + "value": "\"TestEncryptedValue\"" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Encr

+ +
+ +via Bicep module + +```bicep +module automationAccounts './Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-aaencr' + params: { + // Required parameters + name: '<>aaencr001' + // Non-required parameters + cMKKeyName: '' + cMKKeyVaultResourceId: '' + cMKUserAssignedIdentityResourceId: '' + enableDefaultTelemetry: '' + userAssignedIdentities: { + '': {} + } + } +} +``` + +
+

+ +

+ +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": "<>aaencr001" + }, + // Non-required parameters + "cMKKeyName": { + "value": "" + }, + "cMKKeyVaultResourceId": { + "value": "" + }, + "cMKUserAssignedIdentityResourceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "userAssignedIdentities": { + "value": { + "": {} + } + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module automationAccounts './Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-aamin' + params: { + // Required parameters + name: '<>aamin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>aamin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep new file mode 100644 index 000000000..fa9353f9e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/deploy.bicep @@ -0,0 +1,100 @@ +@sys.description('Required. Name of the Automation Account runbook.') +param name string + +@sys.description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@allowed([ + 'Graph' + 'GraphPowerShell' + 'GraphPowerShellWorkflow' + 'PowerShell' + 'PowerShellWorkflow' +]) +@sys.description('Required. The type of the runbook.') +param type string + +@sys.description('Optional. The description of the runbook.') +param description string = '' + +@sys.description('Optional. The uri of the runbook content.') +param uri string = '' + +@sys.description('Optional. The version of the runbook content.') +param version string = '' + +@sys.description('Optional. ID of the runbook storage account.') +param scriptStorageAccountId string = '' + +@sys.description('Generated. Time used as a basis for e.g. the schedule start date.') +param baseTime string = utcNow('u') + +@sys.description('Optional. SAS token validity length. Usage: \'PT8H\' - valid for 8 hours; \'P5D\' - valid for 5 days; \'P1Y\' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours.') +param sasTokenValidityLength string = 'PT8H' + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@sys.description('Optional. Tags of the Automation Account resource.') +param tags object = {} + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var accountSasProperties = { + signedServices: 'b' + signedPermission: 'r' + signedExpiry: dateTimeAdd(baseTime, sasTokenValidityLength) + signedResourceTypes: 'o' + signedProtocol: 'https' +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { + name: automationAccountName +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = if (!empty(scriptStorageAccountId)) { + name: last(split(scriptStorageAccountId, '/'))! + scope: resourceGroup(split(scriptStorageAccountId, '/')[2], split(scriptStorageAccountId, '/')[4]) +} + +var publishContentLink = empty(uri) ? null : { + uri: !empty(uri) ? (empty(scriptStorageAccountId) ? uri : '${uri}?${storageAccount.listAccountSas('2021-04-01', accountSasProperties).accountSasToken}') : null + version: !empty(version) ? version : null +} + +resource runbook 'Microsoft.Automation/automationAccounts/runbooks@2022-08-08' = { + name: name + parent: automationAccount + location: location + tags: tags + properties: { + runbookType: type + description: description + publishContentLink: !empty(uri) ? publishContentLink : null + } +} + +@sys.description('The name of the deployed runbook.') +output name string = runbook.name + +@sys.description('The resource ID of the deployed runbook.') +output resourceId string = runbook.id + +@sys.description('The resource group of the deployed runbook.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The location the resource was deployed into.') +output location string = runbook.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/readme.md new file mode 100644 index 000000000..d00bc8f5e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/readme.md @@ -0,0 +1,105 @@ +# Automation Account Runbooks `[Microsoft.Automation/automationAccounts/runbooks]` + +This module deploys an Azure Automation Account Runbook. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/runbooks` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/runbooks) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | Name of the Automation Account runbook. | +| `type` | string | `[Graph, GraphPowerShell, GraphPowerShellWorkflow, PowerShell, PowerShellWorkflow]` | The type of the runbook. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description of the runbook. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `sasTokenValidityLength` | string | `'PT8H'` | SAS token validity length. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours. | +| `scriptStorageAccountId` | string | `''` | ID of the runbook storage account. | +| `tags` | object | `{object}` | Tags of the Automation Account resource. | +| `uri` | string | `''` | The uri of the runbook content. | +| `version` | string | `''` | The version of the runbook content. | + +**Generated parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Time used as a basis for e.g. the schedule start date. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed runbook. | +| `resourceGroupName` | string | The resource group of the deployed runbook. | +| `resourceId` | string | The resource ID of the deployed runbook. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/runbooks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/deploy.bicep new file mode 100644 index 000000000..08188c3c9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/deploy.bicep @@ -0,0 +1,84 @@ +@sys.description('Required. Name of the Automation Account schedule.') +param name string + +@sys.description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@sys.description('Optional. The properties of the create Advanced Schedule.') +@metadata({ + monthDays: 'Days of the month that the job should execute on. Must be between 1 and 31.' + monthlyOccurrences: 'Occurrences of days within a month.' + weekDays: 'Days of the week that the job should execute on.' +}) +param advancedSchedule object = {} + +@sys.description('Optional. The description of the schedule.') +param description string = '' + +@sys.description('Optional. The end time of the schedule.') +param expiryTime string = '' + +@allowed([ + 'Day' + 'Hour' + 'Minute' + 'Month' + 'OneTime' + 'Week' +]) +@sys.description('Optional. The frequency of the schedule.') +param frequency string = 'OneTime' + +@sys.description('Optional. Anything.') +param interval int = 0 + +@sys.description('Optional. The start time of the schedule.') +param startTime string = '' + +@sys.description('Optional. The time zone of the schedule.') +param timeZone string = '' + +@sys.description('Generated. Time used as a basis for e.g. the schedule start date.') +param baseTime string = utcNow('u') + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { + name: automationAccountName +} + +resource schedule 'Microsoft.Automation/automationAccounts/schedules@2022-08-08' = { + name: name + parent: automationAccount + properties: { + advancedSchedule: !empty(advancedSchedule) ? advancedSchedule : null + description: !empty(description) ? description : null + expiryTime: !empty(expiryTime) ? expiryTime : null + frequency: !empty(frequency) ? frequency : 'OneTime' + interval: (interval != 0) ? interval : null + startTime: !empty(startTime) ? startTime : dateTimeAdd(baseTime, 'PT10M') + timeZone: !empty(timeZone) ? timeZone : null + } +} + +@sys.description('The name of the deployed schedule.') +output name string = schedule.name + +@sys.description('The resource ID of the deployed schedule.') +output resourceId string = schedule.id + +@sys.description('The resource group of the deployed schedule.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/readme.md new file mode 100644 index 000000000..c337d0a7a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/readme.md @@ -0,0 +1,62 @@ +# Automation Account Schedules `[Microsoft.Automation/automationAccounts/schedules]` + +This module deploys an Azure Automation Account Schedule. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/schedules` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/schedules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Automation Account schedule. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `advancedSchedule` | object | `{object}` | | The properties of the create Advanced Schedule. | +| `description` | string | `''` | | The description of the schedule. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `expiryTime` | string | `''` | | The end time of the schedule. | +| `frequency` | string | `'OneTime'` | `[Day, Hour, Minute, Month, OneTime, Week]` | The frequency of the schedule. | +| `interval` | int | `0` | | Anything. | +| `startTime` | string | `''` | | The start time of the schedule. | +| `timeZone` | string | `''` | | The time zone of the schedule. | + +**Generated parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Time used as a basis for e.g. the schedule start date. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed schedule. | +| `resourceGroupName` | string | The resource group of the deployed schedule. | +| `resourceId` | string | The resource ID of the deployed schedule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/schedules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep new file mode 100644 index 000000000..9f2451720 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/deploy.bicep @@ -0,0 +1,273 @@ +@description('Required. The name of the Deployment schedule.') +param name string + +@description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@description('Required. The operating system to be configured by the deployment schedule.') +@allowed([ + 'Windows' + 'Linux' +]) +param operatingSystem string + +@description('Required. Reboot setting for the deployment schedule.') +@allowed([ + 'IfRequired' + 'Never' + 'RebootOnly' + 'Always' +]) +param rebootSetting string + +@description('Required. The frequency of the deployment schedule. When using \'Hour\', \'Day\', \'Week\' or \'Month\', an interval needs to be provided.') +@allowed([ + 'OneTime' + 'Hour' + 'Day' + 'Week' + 'Month' +]) +param frequency string + +@description('Optional. Maximum time allowed for the deployment schedule to run. Duration needs to be specified using the format PT[n]H[n]M[n]S as per ISO8601.') +param maintenanceWindow string = 'PT2H' + +@description('Optional. Update classification included in the deployment schedule.') +@allowed([ + 'Critical' + 'Security' + 'UpdateRollup' + 'FeaturePack' + 'ServicePack' + 'Definition' + 'Tools' + 'Updates' + 'Other' +]) +param updateClassifications array = [ + 'Critical' + 'Security' +] + +@description('Optional. KB numbers or Linux packages excluded in the deployment schedule.') +param excludeUpdates array = [] + +@description('Optional. KB numbers or Linux packages included in the deployment schedule.') +param includeUpdates array = [] + +@description('Optional. Specify the resources to scope the deployment schedule to.') +param scopeByResources array = [ + subscription().id +] + +@description('Optional. Specify tags to which to scope the deployment schedule to.') +param scopeByTags object = {} + +@description('Optional. Enables the scopeByTags to require All (Tag A and Tag B) or Any (Tag A or Tag B).') +@allowed([ + 'All' + 'Any' +]) +param scopeByTagsOperation string = 'All' + +@description('Optional. Specify locations to which to scope the deployment schedule to.') +param scopeByLocations array = [] + +@description('Optional. Parameters provided to the task running before the deployment schedule.') +param preTaskParameters object = {} + +@description('Optional. The source of the task running before the deployment schedule.') +param preTaskSource string = '' + +@description('Optional. Parameters provided to the task running after the deployment schedule.') +param postTaskParameters object = {} + +@description('Optional. The source of the task running after the deployment schedule.') +param postTaskSource string = '' + +@description('Optional. The interval of the frequency for the deployment schedule. 1 Hour is every hour, 2 Day is every second day, etc.') +@maxValue(100) +param interval int = 1 + +@description('Optional. Enables the deployment schedule.') +param isEnabled bool = true + +@description('Optional. Time zone for the deployment schedule. IANA ID or a Windows Time Zone ID.') +param timeZone string = 'UTC' + +@description('Optional. Array of functions from a Log Analytics workspace, used to scope the deployment schedule.') +param nonAzureQueries array = [] + +@description('Optional. List of azure resource IDs for azure virtual machines in scope for the deployment schedule.') +param azureVirtualMachines array = [] + +@description('Optional. List of names of non-azure machines in scope for the deployment schedule.') +param nonAzureComputerNames array = [] + +@description('Optional. Required when used with frequency \'Week\'. Specified the day of the week to run the deployment schedule.') +@allowed([ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + 'Saturday' + 'Sunday' +]) +param weekDays array = [] + +@description('Optional. Can be used with frequency \'Month\'. Provides the specific days of the month to run the deployment schedule.') +@allowed([ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 +]) +param monthDays array = [] + +@description('Optional. Can be used with frequency \'Month\'. Provides the pattern/cadence for running the deployment schedule in a month. Takes objects formed like this {occurance(int),day(string)}. Day is the name of the day to run the deployment schedule, the occurance specifies which occurance of that day to run the deployment schedule.') +param monthlyOccurrences array = [] + +@description('Optional. The start time of the deployment schedule in ISO 8601 format. To specify a specific time use YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. For schedules where we want to start the deployment as soon as possible, specify the time segment only in 24 hour format, HH:MM, 22:00.') +param startTime string = '' + +@description('Optional. The end time of the deployment schedule in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00.') +param expiryTime string = '' + +@description('Optional. The expiry time\'s offset in minutes.') +param expiryTimeOffsetMinutes int = 0 + +@description('Optional. The next time the deployment schedule runs in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00.') +param nextRun string = '' + +@description('Optional. The next run\'s offset in minutes.') +param nextRunOffsetMinutes int = 0 + +@description('Optional. The schedules description.') +param scheduleDescription string = '' + +@description('Generated. Do not touch. Is used to provide the base time for time comparison for startTime. If startTime is specified in HH:MM format, baseTime is used to check if the provided startTime has passed, adding one day before setting the deployment schedule.') +param baseTime string = utcNow('u') + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var updateClassificationsVar = replace(replace(replace(replace(string(updateClassifications), ',', ', '), '[', ''), ']', ''), '"', '') + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { + name: automationAccountName +} + +resource softwareUpdateConfiguration 'Microsoft.Automation/automationAccounts/softwareUpdateConfigurations@2019-06-01' = { + name: name + parent: automationAccount + properties: { + updateConfiguration: { + operatingSystem: operatingSystem + duration: maintenanceWindow + linux: ((operatingSystem == 'Linux') ? { + excludedPackageNameMasks: excludeUpdates + includedPackageNameMasks: includeUpdates + includedPackageClassifications: updateClassificationsVar + rebootSetting: rebootSetting + } : null) + windows: ((operatingSystem == 'Windows') ? { + excludedKbNumbers: excludeUpdates + includedKbNumbers: includeUpdates + includedUpdateClassifications: updateClassificationsVar + rebootSetting: rebootSetting + } : null) + targets: { + azureQueries: [ + { + scope: scopeByResources + tagSettings: { + tags: scopeByTags + filterOperator: scopeByTagsOperation + } + locations: scopeByLocations + } + ] + nonAzureQueries: nonAzureQueries + } + azureVirtualMachines: azureVirtualMachines + nonAzureComputerNames: nonAzureComputerNames + } + tasks: { + preTask: { + parameters: (empty(preTaskParameters) ? null : preTaskParameters) + source: (empty(preTaskSource) ? null : preTaskSource) + } + postTask: { + parameters: (empty(postTaskParameters) ? null : postTaskParameters) + source: (empty(postTaskSource) ? null : postTaskSource) + } + } + scheduleInfo: { + interval: interval + frequency: frequency + isEnabled: isEnabled + timeZone: timeZone + advancedSchedule: { + weekDays: (empty(weekDays) ? null : weekDays) + monthDays: (empty(monthDays) ? null : monthDays) + monthlyOccurrences: (empty(monthlyOccurrences) ? null : monthlyOccurrences) + } + startTime: (empty(startTime) ? dateTimeAdd(baseTime, 'PT10M') : startTime) + expiryTime: expiryTime + expiryTimeOffsetMinutes: expiryTimeOffsetMinutes + nextRun: nextRun + nextRunOffsetMinutes: nextRunOffsetMinutes + description: scheduleDescription + } + } +} + +@description('The name of the deployed softwareUpdateConfiguration.') +output name string = softwareUpdateConfiguration.name + +@description('The resource ID of the deployed softwareUpdateConfiguration.') +output resourceId string = softwareUpdateConfiguration.id + +@description('The resource group of the deployed softwareUpdateConfiguration.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md new file mode 100644 index 000000000..c10375008 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/readme.md @@ -0,0 +1,181 @@ +# Automation Account Software Update Configurations `[Microsoft.Automation/automationAccounts/softwareUpdateConfigurations]` + +This module deploys an Azure Automation Account Software update Configuration. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/softwareUpdateConfigurations` | [2019-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2019-06-01/automationAccounts/softwareUpdateConfigurations) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `frequency` | string | `[Day, Hour, Month, OneTime, Week]` | The frequency of the deployment schedule. When using 'Hour', 'Day', 'Week' or 'Month', an interval needs to be provided. | +| `name` | string | | The name of the Deployment schedule. | +| `operatingSystem` | string | `[Linux, Windows]` | The operating system to be configured by the deployment schedule. | +| `rebootSetting` | string | `[Always, IfRequired, Never, RebootOnly]` | Reboot setting for the deployment schedule. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `azureVirtualMachines` | array | `[]` | | List of azure resource IDs for azure virtual machines in scope for the deployment schedule. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `excludeUpdates` | array | `[]` | | KB numbers or Linux packages excluded in the deployment schedule. | +| `expiryTime` | string | `''` | | The end time of the deployment schedule in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. | +| `expiryTimeOffsetMinutes` | int | `0` | | The expiry time's offset in minutes. | +| `includeUpdates` | array | `[]` | | KB numbers or Linux packages included in the deployment schedule. | +| `interval` | int | `1` | | The interval of the frequency for the deployment schedule. 1 Hour is every hour, 2 Day is every second day, etc. | +| `isEnabled` | bool | `True` | | Enables the deployment schedule. | +| `maintenanceWindow` | string | `'PT2H'` | | Maximum time allowed for the deployment schedule to run. Duration needs to be specified using the format PT[n]H[n]M[n]S as per ISO8601. | +| `monthDays` | array | `[]` | `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]` | Can be used with frequency 'Month'. Provides the specific days of the month to run the deployment schedule. | +| `monthlyOccurrences` | array | `[]` | | Can be used with frequency 'Month'. Provides the pattern/cadence for running the deployment schedule in a month. Takes objects formed like this {occurance(int),day(string)}. Day is the name of the day to run the deployment schedule, the occurance specifies which occurance of that day to run the deployment schedule. | +| `nextRun` | string | `''` | | The next time the deployment schedule runs in ISO 8601 format. YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. | +| `nextRunOffsetMinutes` | int | `0` | | The next run's offset in minutes. | +| `nonAzureComputerNames` | array | `[]` | | List of names of non-azure machines in scope for the deployment schedule. | +| `nonAzureQueries` | array | `[]` | | Array of functions from a Log Analytics workspace, used to scope the deployment schedule. | +| `postTaskParameters` | object | `{object}` | | Parameters provided to the task running after the deployment schedule. | +| `postTaskSource` | string | `''` | | The source of the task running after the deployment schedule. | +| `preTaskParameters` | object | `{object}` | | Parameters provided to the task running before the deployment schedule. | +| `preTaskSource` | string | `''` | | The source of the task running before the deployment schedule. | +| `scheduleDescription` | string | `''` | | The schedules description. | +| `scopeByLocations` | array | `[]` | | Specify locations to which to scope the deployment schedule to. | +| `scopeByResources` | array | `[[subscription().id]]` | | Specify the resources to scope the deployment schedule to. | +| `scopeByTags` | object | `{object}` | | Specify tags to which to scope the deployment schedule to. | +| `scopeByTagsOperation` | string | `'All'` | `[All, Any]` | Enables the scopeByTags to require All (Tag A and Tag B) or Any (Tag A or Tag B). | +| `startTime` | string | `''` | | The start time of the deployment schedule in ISO 8601 format. To specify a specific time use YYYY-MM-DDTHH:MM:SS, 2021-12-31T23:00:00. For schedules where we want to start the deployment as soon as possible, specify the time segment only in 24 hour format, HH:MM, 22:00. | +| `timeZone` | string | `'UTC'` | | Time zone for the deployment schedule. IANA ID or a Windows Time Zone ID. | +| `updateClassifications` | array | `[Critical, Security]` | `[Critical, Definition, FeaturePack, Other, Security, ServicePack, Tools, UpdateRollup, Updates]` | Update classification included in the deployment schedule. | +| `weekDays` | array | `[]` | `[Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday]` | Required when used with frequency 'Week'. Specified the day of the week to run the deployment schedule. | + +**Generated parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('u')]` | Do not touch. Is used to provide the base time for time comparison for startTime. If startTime is specified in HH:MM format, baseTime is used to check if the provided startTime has passed, adding one day before setting the deployment schedule. | + + +### Parameter Usage: `scopeByTags` + +Provide tag keys, with an array of values, filtering in machines that should be included in the deployment schedule. + +| Property name | Type | Possible values | Description | +| :------------ | :---- | :-------------- | :---------- | +| \ | array | string | tag values | + + +

+ +Parameter JSON format + +```json +"scopeByTags": { + "value": { + "Update": [ + "Automatic" + ], + "MaintenanceWindow": [ + "1-Sat-22" + ] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +scopeByTags: { + Update: [ + 'Automatic' + ] + MaintenanceWindow: [ + '1-Sat-22' + ] +} +``` + +
+

+ +### Parameter Usage: `monthlyOccurrences` + +Occurrences of days within a month. + +| Property name | Type | Possible values | Description | +| :------------ | :----- | :------------------------------------------------------------- | :----------------------------------------------------------------------------------- | +| `occurance` | int | 1-5 | Occurrence of the week within the month. Must be between 1 and 5, where 5 is "last". | +| `day` | string | Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday | Day of the occurrence. | + +

+ +Parameter JSON format + +```json +"monthlyOccurrences": { + "value": [ + { + "occurrence": 1, + "day": "Monday" + }, + { + "occurrence": 2, + "day": "Friday" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +monthlyOccurrences: [ + { + occurrence: 1 + day: 'Monday' + } + { + occurrence: 2 + day: 'Friday' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed softwareUpdateConfiguration. | +| `resourceGroupName` | string | The resource group of the deployed softwareUpdateConfiguration. | +| `resourceId` | string | The resource ID of the deployed softwareUpdateConfiguration. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/softwareUpdateConfigurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/deploy.bicep new file mode 100644 index 000000000..6da71a13d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/deploy.bicep @@ -0,0 +1,53 @@ +@sys.description('Conditional. The name of the parent Automation Account. Required if the template is used in a standalone deployment.') +param automationAccountName string + +@sys.description('Required. The name of the variable.') +param name string + +@secure() +@sys.description('Required. The value of the variable. For security best practices, this value is always passed as a secure string as it could contain an encrypted value when the "isEncrypted" property is set to true.') +param value string + +@sys.description('Optional. The description of the variable.') +param description string = '' + +@sys.description('Optional. If the variable should be encrypted. For security reasons encryption of variables should be enabled.') +param isEncrypted bool = true + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { + name: automationAccountName +} + +resource variable 'Microsoft.Automation/automationAccounts/variables@2022-08-08' = { + name: name + parent: automationAccount + properties: { + description: description + isEncrypted: isEncrypted + value: value + } +} + +@sys.description('The name of the deployed variable.') +output name string = variable.name + +@sys.description('The resource ID of the deployed variable.') +output resourceId string = variable.id + +@sys.description('The resource group of the deployed variable.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/readme.md new file mode 100644 index 000000000..f130fcefc --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/readme.md @@ -0,0 +1,103 @@ +# Automation Account Variables `[Microsoft.Automation/automationAccounts/variables]` + +This module deploys a variable to an Azure Automation Account. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Automation/automationAccounts/variables` | [2022-08-08](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Automation/2022-08-08/automationAccounts/variables) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the variable. | +| `value` | securestring | The value of the variable. For security best practices, this value is always passed as a secure string as it could contain an encrypted value when the "isEncrypted" property is set to true. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `automationAccountName` | string | The name of the parent Automation Account. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | The description of the variable. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `isEncrypted` | bool | `True` | If the variable should be encrypted. For security reasons encryption of variables should be enabled. | + + +### Parameter Usage: `value` + +

+ +Parameter JSON format + +```json +//Boolean format +"value": { + "value": "false" +} + +//DateTime format +"value": { + "value": "\"\\/Date(1637934042656)\\/\"" +} + +//Integer format +"value": { + "value": "500" +} + +//String format +"value": { + "value": "\"TestString\"" +} +``` + +
+ +
+ +Bicep format + +```bicep +//Boolean format +value: 'false' + +//DateTime format +value: '\'\\/Date(1637934042656)\\/\'' + +//Integer format +value: '500' + +//String format +value: '\'TestString\'' +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed variable. | +| `resourceGroupName` | string | The resource group of the deployed variable. | +| `resourceId` | string | The resource ID of the deployed variable. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/variables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Automation/automationAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..368b6efea --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource actionGroup 'microsoft.insights/actionGroups@2019-06-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(actionGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: actionGroup +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/deploy.test.bicep new file mode 100644 index 000000000..da3edaad1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/common/deploy.test.bicep @@ -0,0 +1,82 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.actiongroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iagcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + groupShortName: 'ag${serviceShort}001' + emailReceivers: [ + { + emailAddress: 'test.user@testcompany.com' + name: 'TestUser_-EmailAction-' + useCommonAlertSchema: true + } + { + emailAddress: 'test.user2@testcompany.com' + name: 'TestUser2' + useCommonAlertSchema: true + } + ] + roleAssignments: [ + { + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + smsReceivers: [ + { + countryCode: '1' + name: 'TestUser_-SMSAction-' + phoneNumber: '2345678901' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/min/deploy.test.bicep new file mode 100644 index 000000000..80cefe2ff --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/.test/min/deploy.test.bicep @@ -0,0 +1,43 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.actiongroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iagmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + groupShortName: 'ag${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/deploy.bicep new file mode 100644 index 000000000..0ae941d8f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/deploy.bicep @@ -0,0 +1,107 @@ +@description('Required. The name of the action group.') +param name string + +@description('Required. The short name of the action group.') +param groupShortName string + +@description('Optional. Indicates whether this action group is enabled. If an action group is not enabled, then none of its receivers will receive communications.') +param enabled bool = true + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. The list of email receivers that are part of this action group.') +param emailReceivers array = [] + +@description('Optional. The list of SMS receivers that are part of this action group.') +param smsReceivers array = [] + +@description('Optional. The list of webhook receivers that are part of this action group.') +param webhookReceivers array = [] + +@description('Optional. The list of ITSM receivers that are part of this action group.') +param itsmReceivers array = [] + +@description('Optional. The list of AzureAppPush receivers that are part of this action group.') +param azureAppPushReceivers array = [] + +@description('Optional. The list of AutomationRunbook receivers that are part of this action group.') +param automationRunbookReceivers array = [] + +@description('Optional. The list of voice receivers that are part of this action group.') +param voiceReceivers array = [] + +@description('Optional. The list of logic app receivers that are part of this action group.') +param logicAppReceivers array = [] + +@description('Optional. The list of function receivers that are part of this action group.') +param azureFunctionReceivers array = [] + +@description('Optional. The list of ARM role receivers that are part of this action group. Roles are Azure RBAC roles and only built-in roles are supported.') +param armRoleReceivers array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Location for all resources.') +param location string = 'global' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource actionGroup 'microsoft.insights/actionGroups@2019-06-01' = { + name: name + location: location + tags: tags + properties: { + groupShortName: groupShortName + enabled: enabled + emailReceivers: (empty(emailReceivers) ? null : emailReceivers) + smsReceivers: (empty(smsReceivers) ? null : smsReceivers) + webhookReceivers: (empty(webhookReceivers) ? null : webhookReceivers) + itsmReceivers: (empty(itsmReceivers) ? null : itsmReceivers) + azureAppPushReceivers: (empty(azureAppPushReceivers) ? null : azureAppPushReceivers) + automationRunbookReceivers: (empty(automationRunbookReceivers) ? null : automationRunbookReceivers) + voiceReceivers: (empty(voiceReceivers) ? null : voiceReceivers) + logicAppReceivers: (empty(logicAppReceivers) ? null : logicAppReceivers) + azureFunctionReceivers: (empty(azureFunctionReceivers) ? null : azureFunctionReceivers) + armRoleReceivers: (empty(armRoleReceivers) ? null : armRoleReceivers) + } +} + +module actionGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ActionGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: actionGroup.id + } +}] + +@description('The resource group the action group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the action group .') +output name string = actionGroup.name + +@description('The resource ID of the action group .') +output resourceId string = actionGroup.id + +@description('The location the resource was deployed into.') +output location string = actionGroup.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/readme.md new file mode 100644 index 000000000..c85e5bdaf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/readme.md @@ -0,0 +1,413 @@ +# Action Groups `[Microsoft.Insights/actionGroups]` + +This module deploys an Action Group. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.insights/actionGroups` | [2019-06-01](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2019-06-01/actionGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `groupShortName` | string | The short name of the action group. | +| `name` | string | The name of the action group. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `armRoleReceivers` | array | `[]` | The list of ARM role receivers that are part of this action group. Roles are Azure RBAC roles and only built-in roles are supported. | +| `automationRunbookReceivers` | array | `[]` | The list of AutomationRunbook receivers that are part of this action group. | +| `azureAppPushReceivers` | array | `[]` | The list of AzureAppPush receivers that are part of this action group. | +| `azureFunctionReceivers` | array | `[]` | The list of function receivers that are part of this action group. | +| `emailReceivers` | array | `[]` | The list of email receivers that are part of this action group. | +| `enabled` | bool | `True` | Indicates whether this action group is enabled. If an action group is not enabled, then none of its receivers will receive communications. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `itsmReceivers` | array | `[]` | The list of ITSM receivers that are part of this action group. | +| `location` | string | `'global'` | Location for all resources. | +| `logicAppReceivers` | array | `[]` | The list of logic app receivers that are part of this action group. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `smsReceivers` | array | `[]` | The list of SMS receivers that are part of this action group. | +| `tags` | object | `{object}` | Tags of the resource. | +| `voiceReceivers` | array | `[]` | The list of voice receivers that are part of this action group. | +| `webhookReceivers` | array | `[]` | The list of webhook receivers that are part of this action group. | + + +### Parameter Usage: receivers + +See [Documentation](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2019-06-01/actiongroups) for description of parameters usage and syntax. + +

+ +Parameter JSON file + +```json +"emailReceivers": { + "value": [ + { + "name": "TestUser_-EmailAction-", + "emailAddress": "test.user@testcompany.com", + "useCommonAlertSchema": true + }, + { + "name": "TestUser2", + "emailAddress": "test.user2@testcompany.com", + "useCommonAlertSchema": true + } + ] +}, +"smsReceivers": { + "value": [ + { + "name": "TestUser_-SMSAction-", + "countryCode": "1", + "phoneNumber": "2345678901" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +emailReceivers: [ + { + name: 'TestUser_-EmailAction-' + emailAddress: 'test.user@testcompany.com' + useCommonAlertSchema: true + } + { + name: 'TestUser2' + emailAddress: 'test.user2@testcompany.com' + useCommonAlertSchema: true + } +] +smsReceivers: [ + { + name: 'TestUser_-SMSAction-' + countryCode: '1' + phoneNumber: '2345678901' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Additional notes on parameters + +- Receiver name must be unique across the ActionGroup +- Email, SMS, Azure App push and Voice can be grouped in the same Action. To do so, the `name` field of the receivers must be in the `RecName_-ActionType-` format where: + - _RecName_ is the name you want to give to the Action + - _ActionType_ is one of the action types that can be grouped together. Possible values are: + - EmailAction + - SMSAction + - AzureAppAction + - VoiceAction +- To understand the impact of the `useCommonAlertSchema` field, see [here](https://learn.microsoft.com/en-us/azure/azure-monitor/platform/alerts-common-schema) + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the action group . | +| `resourceGroupName` | string | The resource group the action group was deployed into. | +| `resourceId` | string | The resource ID of the action group . | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module actionGroups './Microsoft.Insights/actionGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iagcom' + params: { + // Required parameters + groupShortName: 'agiagcom001' + name: '<>iagcom001' + // Non-required parameters + emailReceivers: [ + { + emailAddress: 'test.user@testcompany.com' + name: 'TestUser_-EmailAction-' + useCommonAlertSchema: true + } + { + emailAddress: 'test.user2@testcompany.com' + name: 'TestUser2' + useCommonAlertSchema: true + } + ] + enableDefaultTelemetry: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + smsReceivers: [ + { + countryCode: '1' + name: 'TestUser_-SMSAction-' + phoneNumber: '2345678901' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "groupShortName": { + "value": "agiagcom001" + }, + "name": { + "value": "<>iagcom001" + }, + // Non-required parameters + "emailReceivers": { + "value": [ + { + "emailAddress": "test.user@testcompany.com", + "name": "TestUser_-EmailAction-", + "useCommonAlertSchema": true + }, + { + "emailAddress": "test.user2@testcompany.com", + "name": "TestUser2", + "useCommonAlertSchema": true + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "smsReceivers": { + "value": [ + { + "countryCode": "1", + "name": "TestUser_-SMSAction-", + "phoneNumber": "2345678901" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module actionGroups './Microsoft.Insights/actionGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iagmin' + params: { + // Required parameters + groupShortName: 'agiagmin001' + name: '<>iagmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "groupShortName": { + "value": "agiagmin001" + }, + "name": { + "value": "<>iagmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/actionGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..0212b972d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource activityLogAlert 'Microsoft.Insights/activityLogAlerts@2020-10-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(activityLogAlert.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: activityLogAlert +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/dependencies.bicep new file mode 100644 index 000000000..f03108936 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/dependencies.bicep @@ -0,0 +1,28 @@ +@description('Required. The name of the Action Group to create.') +param actionGroupName string + +@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 +} + +resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = { + name: actionGroupName + location: 'global' + properties: { + groupShortName: substring(replace(actionGroupName, '-', ''), 0, 11) + enabled: true + } +} + +@description('The resource ID of the created Action Group.') +output actionGroupResourceId string = actionGroup.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/deploy.test.bicep new file mode 100644 index 000000000..4eee2a549 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/.test/common/deploy.test.bicep @@ -0,0 +1,87 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.activityLogAlerts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ialacom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + actionGroupName: 'dep-<>-ag-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + + conditions: [ + { + equals: 'Administrative' + field: 'category' + } + { + equals: 'microsoft.compute/virtualmachines' + field: 'resourceType' + } + { + equals: 'Microsoft.Compute/virtualMachines/performMaintenance/action' + field: 'operationName' + } + ] + actions: [ + { + actionGroupId: nestedDependencies.outputs.actionGroupResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + scopes: [ + subscription().id + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/deploy.bicep new file mode 100644 index 000000000..a5ca6660a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/deploy.bicep @@ -0,0 +1,90 @@ +@description('Required. The name of the alert.') +param name string + +@description('Optional. Description of the alert.') +param alertDescription string = '' + +@description('Optional. Location for all resources.') +param location string = 'global' + +@description('Optional. Indicates whether this alert is enabled.') +param enabled bool = true + +@description('Optional. The list of resource IDs that this Activity Log Alert is scoped to.') +param scopes array = [ + subscription().id +] + +@description('Optional. The list of actions to take when alert triggers.') +param actions array = [] + +@description('Required. The condition that will cause this alert to activate. Array of objects.') +param conditions array + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var actionGroups = [for action in actions: { + actionGroupId: contains(action, 'actionGroupId') ? action.actionGroupId : action + webhookProperties: contains(action, 'webhookProperties') ? action.webhookProperties : null +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource activityLogAlert 'Microsoft.Insights/activityLogAlerts@2020-10-01' = { + name: name + location: location + tags: tags + properties: { + scopes: scopes + condition: { + allOf: conditions + } + actions: { + actionGroups: actionGroups + } + enabled: enabled + description: alertDescription + } +} + +module activityLogAlert_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ActivityLogAlert-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: activityLogAlert.id + } +}] + +@description('The name of the activity log alert.') +output name string = activityLogAlert.name + +@description('The resource ID of the activity log alert.') +output resourceId string = activityLogAlert.id + +@description('The resource group the activity log alert was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = activityLogAlert.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/readme.md new file mode 100644 index 000000000..a9a5a9cee --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/readme.md @@ -0,0 +1,532 @@ +# Activity Log Alerts `[Microsoft.Insights/activityLogAlerts]` + +This module deploys an Alert based on Activity Log. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Insights/activityLogAlerts` | [2020-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-10-01/activityLogAlerts) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `conditions` | array | The condition that will cause this alert to activate. Array of objects. | +| `name` | string | The name of the alert. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | The list of actions to take when alert triggers. | +| `alertDescription` | string | `''` | Description of the alert. | +| `enabled` | bool | `True` | Indicates whether this alert is enabled. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `'global'` | Location for all resources. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `scopes` | array | `[[subscription().id]]` | The list of resource IDs that this Activity Log Alert is scoped to. | +| `tags` | object | `{object}` | Tags of the resource. | + + +### Parameter Usage: actions + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + { + "actionGroupId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName", + "webhookProperties": {} + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +actions: [ + { + actionGroupId: '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName' + webhookProperties: {} + } +] +``` + +
+

+ +`webhookProperties` is optional. + +If you do only want to provide actionGroupIds, a shorthand use of the parameter is available. + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName" + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +actions: [ + '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName' +] +``` + +
+

+ +### Parameter Usage: conditions + +**Conditions can also be combined with logical operators `allOf` and `anyOf`** + + +

+ +Parameter JSON format + +```json +{ + "field": "string", + "equals": "string", + "containsAny": "array" +} +``` + +
+ +
+ +Bicep format + +```bicep +{ + field: 'string' + equals: 'string' + containsAny: 'array' +} +``` + +
+

+ +Each condition can specify only one field between `equals` and `containsAny`. + +| Parameter Name | Type | Possible values | Description | +| :------------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------- | +| `field` | string | `resourceId`,
`category`,
`caller`,
`level`,
`operationName`,
`resourceGroup`,
`resourceProvider`,
`status`,
`subStatus`,
`resourceType`,
or anything beginning with `properties.` | Required. The name of the field that this condition will examine. | +| `equals` | string | | Optional (Alternative to `containsAny`). The value to confront with. | +| `containsAny` | array of strings | | Optional (Alternative to `equals`). Condition will be satisfied if value of the field in the event is within one of the specified here. | + +**Sample** + +
+ +Parameter JSON format + +```json +"conditions": { + "value": [ + { + "field": "category", + "equals": "Administrative" + }, + { + "field": "resourceType", + "equals": "microsoft.compute/virtualmachines" + }, + { + "field": "operationName", + "equals": "Microsoft.Compute/virtualMachines/performMaintenance/action" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +conditions: [ + { + field: 'category' + equals: 'Administrative' + } + { + field: 'resourceType' + equals: 'microsoft.compute/virtualmachines' + } + { + field: 'operationName' + equals: 'Microsoft.Compute/virtualMachines/performMaintenance/action' + } +] +``` + +
+

+ +**Sample 2** + +

+ +Parameter JSON format + +```json +"conditions":{ + "value": [ + { + "field": "category", + "equals": "ServiceHealth" + }, + { + "anyOf": [ + { + "field": "properties.incidentType", + "equals": "Incident" + }, + { + "field": "properties.incidentType", + "equals": "Maintenance" + } + ] + }, + { + "field": "properties.impactedServices[*].ServiceName", + "containsAny": [ + "Action Groups", + "Activity Logs & Alerts" + ] + }, + { + "field": "properties.impactedServices[*].ImpactedRegions[*].RegionName", + "containsAny": [ + "West Europe", + "Global" + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +conditions: [ + { + field: 'category' + equals: 'ServiceHealth' + } + { + anyOf: [ + { + field: 'properties.incidentType' + equals: 'Incident' + } + { + field: 'properties.incidentType' + equals: 'Maintenance' + } + ] + } + { + field: 'properties.impactedServices[*].ServiceName' + containsAny: [ + 'Action Groups' + 'Activity Logs & Alerts' + ] + } + { + field: 'properties.impactedServices[*].ImpactedRegions[*].RegionName' + containsAny: [ + 'West Europe' + 'Global' + ] + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the activity log alert. | +| `resourceGroupName` | string | The resource group the activity log alert was deployed into. | +| `resourceId` | string | The resource ID of the activity log alert. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module activityLogAlerts './Microsoft.Insights/activityLogAlerts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ialacom' + params: { + // Required parameters + conditions: [ + { + equals: 'Administrative' + field: 'category' + } + { + equals: 'microsoft.compute/virtualmachines' + field: 'resourceType' + } + { + equals: 'Microsoft.Compute/virtualMachines/performMaintenance/action' + field: 'operationName' + } + ] + name: '<>ialacom001' + // Non-required parameters + actions: [ + { + actionGroupId: '' + } + ] + enableDefaultTelemetry: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + scopes: [ + '' + ] + tags: { + Environment: 'Non-Prod' + 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 + "conditions": { + "value": [ + { + "equals": "Administrative", + "field": "category" + }, + { + "equals": "microsoft.compute/virtualmachines", + "field": "resourceType" + }, + { + "equals": "Microsoft.Compute/virtualMachines/performMaintenance/action", + "field": "operationName" + } + ] + }, + "name": { + "value": "<>ialacom001" + }, + // Non-required parameters + "actions": { + "value": [ + { + "actionGroupId": "" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scopes": { + "value": [ + "" + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/activityLogAlerts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..01a40b94f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(appInsights.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: appInsights +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/dependencies.bicep new file mode 100644 index 000000000..9e9a8f251 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/dependencies.bicep @@ -0,0 +1,24 @@ +@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 Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/deploy.test.bicep new file mode 100644 index 000000000..e40e2a3d9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/common/deploy.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.components-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iccom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/dependencies.bicep new file mode 100644 index 000000000..cc2447662 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/deploy.test.bicep new file mode 100644 index 000000000..56d8ef6c8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/.test/min/deploy.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.components-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'icmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + workspaceResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/deploy.bicep new file mode 100644 index 000000000..72c883560 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/deploy.bicep @@ -0,0 +1,118 @@ +@description('Required. Name of the Application Insights.') +param name string + +@description('Optional. Application type.') +@allowed([ + 'web' + 'other' +]) +param applicationType string = 'web' + +@description('Required. Resource ID of the log analytics workspace which the data will be ingested to. This property is required to create an application with this API version. Applications from older versions will not have this property.') +param workspaceResourceId string + +@description('Optional. The network access type for accessing Application Insights ingestion. - Enabled or Disabled.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForIngestion string = 'Enabled' + +@description('Optional. The network access type for accessing Application Insights query. - Enabled or Disabled.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForQuery string = 'Enabled' + +@description('Optional. Retention period in days.') +@allowed([ + 30 + 60 + 90 + 120 + 180 + 270 + 365 + 550 + 730 +]) +param retentionInDays int = 365 + +@description('Optional. Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry.') +@minValue(0) +@maxValue(100) +param samplingPercentage int = 100 + +@description('Optional. The kind of application that this component refers to, used to customize UI. This value is a freeform string, values should typically be one of the following: web, ios, other, store, java, phone.') +param kind string = '' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource appInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: kind + properties: { + Application_Type: applicationType + WorkspaceResourceId: workspaceResourceId + publicNetworkAccessForIngestion: publicNetworkAccessForIngestion + publicNetworkAccessForQuery: publicNetworkAccessForQuery + RetentionInDays: retentionInDays + SamplingPercentage: samplingPercentage + } +} + +module appInsights_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppInsights-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: appInsights.id + } +}] + +@description('The name of the application insights component.') +output name string = appInsights.name + +@description('The resource ID of the application insights component.') +output resourceId string = appInsights.id + +@description('The resource group the application insights component was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The application ID of the application insights component.') +output applicationId string = appInsights.properties.AppId + +@description('The location the resource was deployed into.') +output location string = appInsights.location + +@description('Application Insights Instrumentation key. A read-only value that applications can use to identify the destination for all telemetry sent to Azure Application Insights. This value will be supplied upon construction of each new Application Insights component.') +output instrumentationKey string = appInsights.properties.InstrumentationKey diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/readme.md new file mode 100644 index 000000000..f52fbb764 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/readme.md @@ -0,0 +1,291 @@ +# Application Insights `[Microsoft.Insights/components]` + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Insights/components` | [2020-02-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-02-02/components) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Insights. | +| `workspaceResourceId` | string | Resource ID of the log analytics workspace which the data will be ingested to. This property is required to create an application with this API version. Applications from older versions will not have this property. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `applicationType` | string | `'web'` | `[other, web]` | Application type. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `kind` | string | `''` | | The kind of application that this component refers to, used to customize UI. This value is a freeform string, values should typically be one of the following: web, ios, other, store, java, phone. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `publicNetworkAccessForIngestion` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Application Insights ingestion. - Enabled or Disabled. | +| `publicNetworkAccessForQuery` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Application Insights query. - Enabled or Disabled. | +| `retentionInDays` | int | `365` | `[30, 60, 90, 120, 180, 270, 365, 550, 730]` | Retention period in days. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `samplingPercentage` | int | `100` | | Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `applicationId` | string | The application ID of the application insights component. | +| `instrumentationKey` | string | Application Insights Instrumentation key. A read-only value that applications can use to identify the destination for all telemetry sent to Azure Application Insights. This value will be supplied upon construction of each new Application Insights component. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application insights component. | +| `resourceGroupName` | string | The resource group the application insights component was deployed into. | +| `resourceId` | string | The resource ID of the application insights component. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module components './Microsoft.Insights/components/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iccom' + params: { + // Required parameters + name: '<>iccom001' + workspaceResourceId: '' + // Non-required parameters + enableDefaultTelemetry: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>iccom001" + }, + "workspaceResourceId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module components './Microsoft.Insights/components/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-icmin' + params: { + // Required parameters + name: '<>icmin001' + workspaceResourceId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>icmin001" + }, + "workspaceResourceId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/components/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..cb2c60be9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(dataCollectionEndpoint.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: dataCollectionEndpoint +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/dependencies.bicep new file mode 100644 index 000000000..d16e1031b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/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/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/deploy.test.bicep new file mode 100644 index 000000000..69ea5b72f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/common/deploy.test.bicep @@ -0,0 +1,66 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionEndpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcecom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + managedIdentityName: 'dep-<>-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + publicNetworkAccess: 'Enabled' + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + resourceType: 'Data Collection Rules' + kind: 'Windows' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/min/deploy.test.bicep new file mode 100644 index 000000000..a6611c177 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/.test/min/deploy.test.bicep @@ -0,0 +1,41 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionEndpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcemin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/deploy.bicep new file mode 100644 index 000000000..2d73b9c2d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/deploy.bicep @@ -0,0 +1,106 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the data collection endpoint. The name is case insensitive.') +param name string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The kind of the resource.') +@allowed([ + 'Linux' + 'Windows' +]) +param kind string = 'Linux' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Specify the type of lock.') +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. The configuration to set whether network access from public internet to the endpoints are allowed.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = 'Disabled' + +@description('Optional. Resource tags.') +param tags object = {} + +// =============== // +// Deployments // +// =============== // + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { + kind: kind + location: location + name: name + tags: tags + properties: { + networkAcls: { + publicNetworkAccess: publicNetworkAccess + } + } +} + +resource dataCollectionEndpoint_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${dataCollectionEndpoint.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: dataCollectionEndpoint +} + +module dataCollectionEndpoint_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-dataCollectionEndpoint-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: dataCollectionEndpoint.id + } +}] + +// =========== // +// Outputs // +// =========== // + +@description('The name of the dataCollectionEndpoint.') +output name string = dataCollectionEndpoint.name + +@description('The resource ID of the dataCollectionEndpoint.') +output resourceId string = dataCollectionEndpoint.id + +@description('The name of the resource group the dataCollectionEndpoint was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = dataCollectionEndpoint.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/readme.md new file mode 100644 index 000000000..ff1194fe7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/readme.md @@ -0,0 +1,292 @@ +# DataCollectionEndpoints `[Microsoft.Insights/dataCollectionEndpoints]` + +This module deploys DataCollection Endpoints. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/dataCollectionEndpoints` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-04-01/dataCollectionEndpoints) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the data collection endpoint. The name is case insensitive. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `kind` | string | `'Linux'` | `[Linux, Windows]` | The kind of the resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicNetworkAccess` | string | `'Disabled'` | `[Disabled, Enabled]` | The configuration to set whether network access from public internet to the endpoints are allowed. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the dataCollectionEndpoint. | +| `resourceGroupName` | string | The name of the resource group the dataCollectionEndpoint was created in. | +| `resourceId` | string | The resource ID of the dataCollectionEndpoint. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module dataCollectionEndpoints './Microsoft.Insights/dataCollectionEndpoints/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcecom' + params: { + // Required parameters + name: '<>idcecom001' + // Non-required parameters + enableDefaultTelemetry: '' + kind: 'Windows' + lock: 'CanNotDelete' + publicNetworkAccess: 'Enabled' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + kind: 'Windows' + resourceType: 'Data Collection Rules' + } + } +} +``` + +
+

+ +

+ +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": "<>idcecom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Windows" + }, + "lock": { + "value": "CanNotDelete" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "kind": "Windows", + "resourceType": "Data Collection Rules" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module dataCollectionEndpoints './Microsoft.Insights/dataCollectionEndpoints/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcemin' + params: { + // Required parameters + name: '<>idcemin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>idcemin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionEndpoints/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..83fa78801 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container"') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2021-04-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(dataCollectionRule.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: dataCollectionRule +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/dependencies.bicep new file mode 100644 index 000000000..e31386a91 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/dependencies.bicep @@ -0,0 +1,79 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the data collection endpoint to create.') +param dataCollectionEndpointName string + +@description('Required. The name of the log analytics workspace to create.') +param logAnalyticsWorkspaceName string + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location + + resource customTableAdvanced 'tables@2022-10-01' = { + name: 'CustomTableAdvanced_CL' + properties: { + schema: { + name: 'CustomTableAdvanced_CL' + columns: [ + { + name: 'TimeGenerated' + type: 'DateTime' + } + { + name: 'EventTime' + type: 'DateTime' + } + { + name: 'EventLevel' + type: 'String' + } + { + name: 'EventCode' + type: 'Int' + } + { + name: 'Message' + type: 'String' + } + { + name: 'RawData' + type: 'String' + } + ] + } + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { + kind: 'Windows' + location: location + name: dataCollectionEndpointName + properties: { + networkAcls: { + publicNetworkAccess: 'Enabled' + } + } +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The name of the deployed log analytics workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Data Collection Endpoint.') +output dataCollectionEndpointResourceId string = dataCollectionEndpoint.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/deploy.test.bicep new file mode 100644 index 000000000..9b3d307be --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customadv/deploy.test.bicep @@ -0,0 +1,139 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionRules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcrcusadv' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + dataCollectionEndpointName: 'dep-<>-dce-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + dataCollectionEndpointId: resourceGroupResources.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): ",,,", for example: "2023-01-25T20:15:05Z,ERROR,404,Page not found"' + dataFlows: [ + { + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] + destinations: [ + resourceGroupResources.outputs.logAnalyticsWorkspaceName + ] + transformKql: 'source | extend LogFields = split(RawData, ",") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message' + outputStream: 'Custom-CustomTableAdvanced_CL' + } + ] + dataSources: { + logFiles: [ + { + name: 'CustomTableAdvanced_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] + filePatterns: [ + 'C:\\TestLogsAdvanced\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: resourceGroupResources.outputs.logAnalyticsWorkspaceResourceId + name: resourceGroupResources.outputs.logAnalyticsWorkspaceName + } + ] + } + streamDeclarations: { + 'Custom-CustomTableAdvanced_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'EventTime' + type: 'datetime' + } + { + name: 'EventLevel' + type: 'string' + } + { + name: 'EventCode' + type: 'int' + } + { + name: 'Message' + type: 'string' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } + enableDefaultTelemetry: enableDefaultTelemetry + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + resourceType: 'Data Collection Rules' + kind: 'Windows' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/dependencies.bicep new file mode 100644 index 000000000..f1804cde9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/dependencies.bicep @@ -0,0 +1,63 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the data collection endpoint to create.') +param dataCollectionEndpointName string + +@description('Required. The name of the log analytics workspace to create.') +param logAnalyticsWorkspaceName string + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location + + resource customTableBasic 'tables@2022-10-01' = { + name: 'CustomTableBasic_CL' + properties: { + schema: { + name: 'CustomTableBasic_CL' + columns: [ + { + name: 'TimeGenerated' + type: 'DateTime' + } + { + name: 'RawData' + type: 'String' + } + ] + } + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { + kind: 'Windows' + location: location + name: dataCollectionEndpointName + properties: { + networkAcls: { + publicNetworkAccess: 'Enabled' + } + } +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The name of the deployed log analytics workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Data Collection Endpoint.') +output dataCollectionEndpointResourceId string = dataCollectionEndpoint.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/deploy.test.bicep new file mode 100644 index 000000000..bde3abbf0 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/custombasic/deploy.test.bicep @@ -0,0 +1,123 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionRules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcrcusbas' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + dataCollectionEndpointName: 'dep-<>-dce-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + dataCollectionEndpointId: resourceGroupResources.outputs.dataCollectionEndpointResourceId + description: 'Collecting custom text logs without ingestion-time transformation.' + dataFlows: [ + { + streams: [ + 'Custom-CustomTableBasic_CL' + ] + destinations: [ + resourceGroupResources.outputs.logAnalyticsWorkspaceName + ] + transformKql: 'source' + outputStream: 'Custom-CustomTableBasic_CL' + } + ] + dataSources: { + logFiles: [ + { + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + streams: [ + 'Custom-CustomTableBasic_CL' + ] + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: resourceGroupResources.outputs.logAnalyticsWorkspaceResourceId + name: resourceGroupResources.outputs.logAnalyticsWorkspaceName + } + ] + } + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } + enableDefaultTelemetry: enableDefaultTelemetry + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + resourceType: 'Data Collection Rules' + kind: 'Windows' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/dependencies.bicep new file mode 100644 index 000000000..3da169196 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/dependencies.bicep @@ -0,0 +1,44 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the data collection endpoint to create.') +param dataCollectionEndpointName string + +@description('Required. The name of the log analytics workspace to create.') +param logAnalyticsWorkspaceName string + +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { + kind: 'Windows' + location: location + name: dataCollectionEndpointName + properties: { + networkAcls: { + publicNetworkAccess: 'Enabled' + } + } +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The name of the deployed log analytics workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Data Collection Endpoint.') +output dataCollectionEndpointResourceId string = dataCollectionEndpoint.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/deploy.test.bicep new file mode 100644 index 000000000..947d52e9a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/customiis/deploy.test.bicep @@ -0,0 +1,102 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionRules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcrcusiis' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + dataCollectionEndpointName: 'dep-<>-dce-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + dataCollectionEndpointId: resourceGroupResources.outputs.dataCollectionEndpointResourceId + description: 'Collecting IIS logs.' + dataFlows: [ + { + streams: [ + 'Microsoft-W3CIISLog' + ] + destinations: [ + resourceGroupResources.outputs.logAnalyticsWorkspaceName + ] + transformKql: 'source' + outputStream: 'Microsoft-W3CIISLog' + } + ] + dataSources: { + iisLogs: [ + { + name: 'iisLogsDataSource' + streams: [ + 'Microsoft-W3CIISLog' + ] + logDirectories: [ + 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + ] + } + ] + } + destinations: { + logAnalytics: [ + { + workspaceResourceId: resourceGroupResources.outputs.logAnalyticsWorkspaceResourceId + name: resourceGroupResources.outputs.logAnalyticsWorkspaceName + } + ] + } + enableDefaultTelemetry: enableDefaultTelemetry + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + resourceType: 'Data Collection Rules' + kind: 'Windows' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/dependencies.bicep new file mode 100644 index 000000000..24938ae9b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/dependencies.bicep @@ -0,0 +1,27 @@ +@description('Optional. The location to deploy resources to.') +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 managed identity to create.') +param managedIdentityName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The name of the deployed log analytics workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/deploy.test.bicep new file mode 100644 index 000000000..8c8b91b3a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/linux/deploy.test.bicep @@ -0,0 +1,215 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionRules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcrlin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + description: 'Collecting Linux-specific performance counters and Linux Syslog' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + 'Processor(*)\\% Processor Time' + 'Processor(*)\\% Idle Time' + 'Processor(*)\\% User Time' + 'Processor(*)\\% Nice Time' + 'Processor(*)\\% Privileged Time' + 'Processor(*)\\% IO Wait Time' + 'Processor(*)\\% Interrupt Time' + 'Processor(*)\\% DPC Time' + 'Memory(*)\\Available MBytes Memory' + 'Memory(*)\\% Available Memory' + 'Memory(*)\\Used Memory MBytes' + 'Memory(*)\\% Used Memory' + 'Memory(*)\\Pages/sec' + 'Memory(*)\\Page Reads/sec' + 'Memory(*)\\Page Writes/sec' + 'Memory(*)\\Available MBytes Swap' + 'Memory(*)\\% Available Swap Space' + 'Memory(*)\\Used MBytes Swap Space' + 'Memory(*)\\% Used Swap Space' + 'Logical Disk(*)\\% Free Inodes' + 'Logical Disk(*)\\% Used Inodes' + 'Logical Disk(*)\\Free Megabytes' + 'Logical Disk(*)\\% Free Space' + 'Logical Disk(*)\\% Used Space' + 'Logical Disk(*)\\Logical Disk Bytes/sec' + 'Logical Disk(*)\\Disk Read Bytes/sec' + 'Logical Disk(*)\\Disk Write Bytes/sec' + 'Logical Disk(*)\\Disk Transfers/sec' + 'Logical Disk(*)\\Disk Reads/sec' + 'Logical Disk(*)\\Disk Writes/sec' + 'Network(*)\\Total Bytes Transmitted' + 'Network(*)\\Total Bytes Received' + 'Network(*)\\Total Bytes' + 'Network(*)\\Total Packets Transmitted' + 'Network(*)\\Total Packets Received' + 'Network(*)\\Total Rx Errors' + 'Network(*)\\Total Tx Errors' + 'Network(*)\\Total Collisions' + ] + } + ] + syslog: [ + { + name: 'sysLogsDataSource-debugLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'auth' + 'authpriv' + ] + logLevels: [ + 'Debug' + 'Info' + 'Notice' + 'Warning' + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + { + name: 'sysLogsDataSource-warningLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'cron' + 'daemon' + 'mark' + 'kern' + 'local0' + ] + logLevels: [ + 'Warning' + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + { + name: 'sysLogsDataSource-errLevel' + streams: [ + 'Microsoft-Syslog' + ] + facilityNames: [ + 'local1' + 'local2' + 'local3' + 'local4' + 'local5' + 'local6' + 'local7' + 'lpr' + 'mail' + 'news' + 'syslog' + ] + logLevels: [ + 'Error' + 'Critical' + 'Alert' + 'Emergency' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + workspaceResourceId: resourceGroupResources.outputs.logAnalyticsWorkspaceResourceId + name: resourceGroupResources.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-InsightsMetrics' + ] + destinations: [ + 'azureMonitorMetrics-default' + ] + } + { + streams: [ + 'Microsoft-Syslog' + ] + destinations: [ + resourceGroupResources.outputs.logAnalyticsWorkspaceName + ] + } + + ] + enableDefaultTelemetry: enableDefaultTelemetry + kind: 'Linux' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + resourceType: 'Data Collection Rules' + kind: 'Linux' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/min/deploy.test.bicep new file mode 100644 index 000000000..e4fa4c7e2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/min/deploy.test.bicep @@ -0,0 +1,80 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionRules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcrmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + } + dataFlows: [ + { + streams: [ + 'Microsoft-InsightsMetrics' + ] + destinations: [ + 'azureMonitorMetrics-default' + ] + } + ] + enableDefaultTelemetry: enableDefaultTelemetry + kind: 'Windows' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/dependencies.bicep new file mode 100644 index 000000000..24938ae9b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/dependencies.bicep @@ -0,0 +1,27 @@ +@description('Optional. The location to deploy resources to.') +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 managed identity to create.') +param managedIdentityName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id + +@description('The name of the deployed log analytics workspace.') +output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/deploy.test.bicep new file mode 100644 index 000000000..5fdc924e2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/.test/windows/deploy.test.bicep @@ -0,0 +1,169 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.dataCollectionRules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idcrwin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// =========== // +// Deployments // +// =========== // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +module resourceGroupResources 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-paramNested' + params: { + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + dataSources: { + performanceCounters: [ + { + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + counterSpecifiers: [ + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Processes' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Handle Count' + '\\System\\System Up Time' + '\\System\\Context Switches/sec' + '\\System\\Processor Queue Length' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pages/sec' + '\\Memory\\Page Faults/sec' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords,13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + workspaceResourceId: resourceGroupResources.outputs.logAnalyticsWorkspaceResourceId + name: resourceGroupResources.outputs.logAnalyticsWorkspaceName + } + ] + } + dataFlows: [ + { + streams: [ + 'Microsoft-InsightsMetrics' + ] + destinations: [ + 'azureMonitorMetrics-default' + ] + } + { + streams: [ + 'Microsoft-Event' + ] + destinations: [ + resourceGroupResources.outputs.logAnalyticsWorkspaceName + ] + } + + ] + enableDefaultTelemetry: enableDefaultTelemetry + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + resourceGroupResources.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + resourceType: 'Data Collection Rules' + kind: 'Windows' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/deploy.bicep new file mode 100644 index 000000000..9c5945623 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/deploy.bicep @@ -0,0 +1,120 @@ +// ============== // +// Parameters // +// ============== // + +@sys.description('Required. The name of the data collection rule. The name is case insensitive.') +param name string + +@sys.description('Optional. The resource ID of the data collection endpoint that this rule can be used with.') +param dataCollectionEndpointId string = '' + +@sys.description('Required. The specification of data flows.') +param dataFlows array + +@sys.description('Required. Specification of data sources that will be collected.') +param dataSources object + +@sys.description('Optional. Description of the data collection rule.') +param description string = '' + +@sys.description('Required. Specification of destinations that can be used in data flows.') +param destinations object + +@sys.description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. The kind of the resource.') +@allowed([ + 'Linux' + 'Windows' +]) +param kind string = 'Linux' + +@sys.description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@sys.description('Optional. Specify the type of lock.') +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +param lock string = '' + +@sys.description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@sys.description('Optional. Declaration of custom streams used in this rule.') +param streamDeclarations object = {} + +@sys.description('Optional. Resource tags.') +param tags object = {} + +// =============== // +// Deployments // +// =============== // + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2021-09-01-preview' = { + kind: kind + location: location + name: name + tags: tags + properties: { + dataSources: dataSources + destinations: destinations + dataFlows: dataFlows + dataCollectionEndpointId: !empty(dataCollectionEndpointId) ? dataCollectionEndpointId : null + streamDeclarations: !empty(streamDeclarations) ? streamDeclarations : null + description: !empty(description) ? description : null + } +} + +resource dataCollectionRule_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${dataCollectionRule.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: dataCollectionRule +} + +module dataCollectionRule_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-dataCollectionRule-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: dataCollectionRule.id + } +}] + +// =========== // +// Outputs // +// =========== // + +@sys.description('The name of the dataCollectionRule.') +output name string = dataCollectionRule.name + +@sys.description('The resource ID of the dataCollectionRule.') +output resourceId string = dataCollectionRule.id + +@sys.description('The name of the resource group the dataCollectionRule was created in.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The location the resource was deployed into.') +output location string = dataCollectionRule.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/readme.md new file mode 100644 index 000000000..129b60bc9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/readme.md @@ -0,0 +1,1587 @@ +# DataCollectionRules `[Microsoft.Insights/dataCollectionRules]` + +This module deploys DataCollection Rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/dataCollectionRules` | [2021-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-09-01-preview/dataCollectionRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `dataFlows` | array | The specification of data flows. | +| `dataSources` | object | Specification of data sources that will be collected. | +| `destinations` | object | Specification of destinations that can be used in data flows. | +| `name` | string | The name of the data collection rule. The name is case insensitive. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `dataCollectionEndpointId` | string | `''` | | The resource ID of the data collection endpoint that this rule can be used with. | +| `description` | string | `''` | | Description of the data collection rule. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `kind` | string | `'Linux'` | `[Linux, Windows]` | The kind of the resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `streamDeclarations` | object | `{object}` | | Declaration of custom streams used in this rule. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the dataCollectionRule. | +| `resourceGroupName` | string | The name of the resource group the dataCollectionRule was created in. | +| `resourceId` | string | The resource ID of the dataCollectionRule. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Customadv

+ +
+ +via Bicep module + +```bicep +module dataCollectionRules './Microsoft.Insights/dataCollectionRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcrcusadv' + params: { + // Required parameters + dataFlows: [ + { + destinations: [ + '' + ] + outputStream: 'Custom-CustomTableAdvanced_CL' + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] + transformKql: 'source | extend LogFields = split(RawData \'\') | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated EventTime EventLevel EventCode Message' + } + ] + dataSources: { + logFiles: [ + { + filePatterns: [ + 'C:\\TestLogsAdvanced\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableAdvanced_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableAdvanced_CL' + ] + } + ] + } + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + name: '<>idcrcusadv001' + // Non-required parameters + dataCollectionEndpointId: '' + description: 'Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \'\' for example: \'2023-01-25T20:15:05ZERROR404Page not found\'' + enableDefaultTelemetry: '' + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + streamDeclarations: { + 'Custom-CustomTableAdvanced_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'EventTime' + type: 'datetime' + } + { + name: 'EventLevel' + type: 'string' + } + { + name: 'EventCode' + type: 'int' + } + { + name: 'Message' + type: 'string' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } + tags: { + kind: 'Windows' + resourceType: 'Data Collection Rules' + } + } +} +``` + +
+

+ +

+ +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 + "dataFlows": { + "value": [ + { + "destinations": [ + "" + ], + "outputStream": "Custom-CustomTableAdvanced_CL", + "streams": [ + "Custom-CustomTableAdvanced_CL" + ], + "transformKql": "source | extend LogFields = split(RawData, \",\") | extend EventTime = todatetime(LogFields[0]) | extend EventLevel = tostring(LogFields[1]) | extend EventCode = toint(LogFields[2]) | extend Message = tostring(LogFields[3]) | project TimeGenerated, EventTime, EventLevel, EventCode, Message" + } + ] + }, + "dataSources": { + "value": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsAdvanced\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableAdvanced_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableAdvanced_CL" + ] + } + ] + } + }, + "destinations": { + "value": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + } + }, + "name": { + "value": "<>idcrcusadv001" + }, + // Non-required parameters + "dataCollectionEndpointId": { + "value": "" + }, + "description": { + "value": "Collecting custom text logs with ingestion-time transformation to columns. Expected format of a log line (comma separated values): \",,,\", for example: \"2023-01-25T20:15:05Z,ERROR,404,Page not found\"" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Windows" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "streamDeclarations": { + "value": { + "Custom-CustomTableAdvanced_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "EventTime", + "type": "datetime" + }, + { + "name": "EventLevel", + "type": "string" + }, + { + "name": "EventCode", + "type": "int" + }, + { + "name": "Message", + "type": "string" + }, + { + "name": "RawData", + "type": "string" + } + ] + } + } + }, + "tags": { + "value": { + "kind": "Windows", + "resourceType": "Data Collection Rules" + } + } + } +} +``` + +
+

+ +

Example 2: Custombasic

+ +
+ +via Bicep module + +```bicep +module dataCollectionRules './Microsoft.Insights/dataCollectionRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcrcusbas' + params: { + // Required parameters + dataFlows: [ + { + destinations: [ + '' + ] + outputStream: 'Custom-CustomTableBasic_CL' + streams: [ + 'Custom-CustomTableBasic_CL' + ] + transformKql: 'source' + } + ] + dataSources: { + logFiles: [ + { + filePatterns: [ + 'C:\\TestLogsBasic\\TestLog*.log' + ] + format: 'text' + name: 'CustomTableBasic_CL' + samplingFrequencyInSeconds: 60 + settings: { + text: { + recordStartTimestampFormat: 'ISO 8601' + } + } + streams: [ + 'Custom-CustomTableBasic_CL' + ] + } + ] + } + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + name: '<>idcrcusbas001' + // Non-required parameters + dataCollectionEndpointId: '' + description: 'Collecting custom text logs without ingestion-time transformation.' + enableDefaultTelemetry: '' + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + streamDeclarations: { + 'Custom-CustomTableBasic_CL': { + columns: [ + { + name: 'TimeGenerated' + type: 'datetime' + } + { + name: 'RawData' + type: 'string' + } + ] + } + } + tags: { + kind: 'Windows' + resourceType: 'Data Collection Rules' + } + } +} +``` + +
+

+ +

+ +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 + "dataFlows": { + "value": [ + { + "destinations": [ + "" + ], + "outputStream": "Custom-CustomTableBasic_CL", + "streams": [ + "Custom-CustomTableBasic_CL" + ], + "transformKql": "source" + } + ] + }, + "dataSources": { + "value": { + "logFiles": [ + { + "filePatterns": [ + "C:\\TestLogsBasic\\TestLog*.log" + ], + "format": "text", + "name": "CustomTableBasic_CL", + "samplingFrequencyInSeconds": 60, + "settings": { + "text": { + "recordStartTimestampFormat": "ISO 8601" + } + }, + "streams": [ + "Custom-CustomTableBasic_CL" + ] + } + ] + } + }, + "destinations": { + "value": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + } + }, + "name": { + "value": "<>idcrcusbas001" + }, + // Non-required parameters + "dataCollectionEndpointId": { + "value": "" + }, + "description": { + "value": "Collecting custom text logs without ingestion-time transformation." + }, + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Windows" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "streamDeclarations": { + "value": { + "Custom-CustomTableBasic_CL": { + "columns": [ + { + "name": "TimeGenerated", + "type": "datetime" + }, + { + "name": "RawData", + "type": "string" + } + ] + } + } + }, + "tags": { + "value": { + "kind": "Windows", + "resourceType": "Data Collection Rules" + } + } + } +} +``` + +
+

+ +

Example 3: Customiis

+ +
+ +via Bicep module + +```bicep +module dataCollectionRules './Microsoft.Insights/dataCollectionRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcrcusiis' + params: { + // Required parameters + dataFlows: [ + { + destinations: [ + '' + ] + outputStream: 'Microsoft-W3CIISLog' + streams: [ + 'Microsoft-W3CIISLog' + ] + transformKql: 'source' + } + ] + dataSources: { + iisLogs: [ + { + logDirectories: [ + 'C:\\inetpub\\logs\\LogFiles\\W3SVC1' + ] + name: 'iisLogsDataSource' + streams: [ + 'Microsoft-W3CIISLog' + ] + } + ] + } + destinations: { + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + name: '<>idcrcusiis001' + // Non-required parameters + dataCollectionEndpointId: '' + description: 'Collecting IIS logs.' + enableDefaultTelemetry: '' + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + kind: 'Windows' + resourceType: 'Data Collection Rules' + } + } +} +``` + +
+

+ +

+ +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 + "dataFlows": { + "value": [ + { + "destinations": [ + "" + ], + "outputStream": "Microsoft-W3CIISLog", + "streams": [ + "Microsoft-W3CIISLog" + ], + "transformKql": "source" + } + ] + }, + "dataSources": { + "value": { + "iisLogs": [ + { + "logDirectories": [ + "C:\\inetpub\\logs\\LogFiles\\W3SVC1" + ], + "name": "iisLogsDataSource", + "streams": [ + "Microsoft-W3CIISLog" + ] + } + ] + } + }, + "destinations": { + "value": { + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + } + }, + "name": { + "value": "<>idcrcusiis001" + }, + // Non-required parameters + "dataCollectionEndpointId": { + "value": "" + }, + "description": { + "value": "Collecting IIS logs." + }, + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Windows" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "kind": "Windows", + "resourceType": "Data Collection Rules" + } + } + } +} +``` + +
+

+ +

Example 4: Linux

+ +
+ +via Bicep module + +```bicep +module dataCollectionRules './Microsoft.Insights/dataCollectionRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcrlin' + params: { + // Required parameters + dataFlows: [ + { + destinations: [ + 'azureMonitorMetrics-default' + ] + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + { + destinations: [ + '' + ] + streams: [ + 'Microsoft-Syslog' + ] + } + ] + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + 'Logical Disk(*)\\% Free Inodes' + 'Logical Disk(*)\\% Free Space' + 'Logical Disk(*)\\% Used Inodes' + 'Logical Disk(*)\\% Used Space' + 'Logical Disk(*)\\Disk Read Bytes/sec' + 'Logical Disk(*)\\Disk Reads/sec' + 'Logical Disk(*)\\Disk Transfers/sec' + 'Logical Disk(*)\\Disk Write Bytes/sec' + 'Logical Disk(*)\\Disk Writes/sec' + 'Logical Disk(*)\\Free Megabytes' + 'Logical Disk(*)\\Logical Disk Bytes/sec' + 'Memory(*)\\% Available Memory' + 'Memory(*)\\% Available Swap Space' + 'Memory(*)\\% Used Memory' + 'Memory(*)\\% Used Swap Space' + 'Memory(*)\\Available MBytes Memory' + 'Memory(*)\\Available MBytes Swap' + 'Memory(*)\\Page Reads/sec' + 'Memory(*)\\Page Writes/sec' + 'Memory(*)\\Pages/sec' + 'Memory(*)\\Used MBytes Swap Space' + 'Memory(*)\\Used Memory MBytes' + 'Network(*)\\Total Bytes' + 'Network(*)\\Total Bytes Received' + 'Network(*)\\Total Bytes Transmitted' + 'Network(*)\\Total Collisions' + 'Network(*)\\Total Packets Received' + 'Network(*)\\Total Packets Transmitted' + 'Network(*)\\Total Rx Errors' + 'Network(*)\\Total Tx Errors' + 'Processor(*)\\% DPC Time' + 'Processor(*)\\% Idle Time' + 'Processor(*)\\% Interrupt Time' + 'Processor(*)\\% IO Wait Time' + 'Processor(*)\\% Nice Time' + 'Processor(*)\\% Privileged Time' + 'Processor(*)\\% Processor Time' + 'Processor(*)\\% User Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + syslog: [ + { + facilityNames: [ + 'auth' + 'authpriv' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Debug' + 'Emergency' + 'Error' + 'Info' + 'Notice' + 'Warning' + ] + name: 'sysLogsDataSource-debugLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + { + facilityNames: [ + 'cron' + 'daemon' + 'kern' + 'local0' + 'mark' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Emergency' + 'Error' + 'Warning' + ] + name: 'sysLogsDataSource-warningLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + { + facilityNames: [ + 'local1' + 'local2' + 'local3' + 'local4' + 'local5' + 'local6' + 'local7' + 'lpr' + 'mail' + 'news' + 'syslog' + ] + logLevels: [ + 'Alert' + 'Critical' + 'Emergency' + 'Error' + ] + name: 'sysLogsDataSource-errLevel' + streams: [ + 'Microsoft-Syslog' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + name: '<>idcrlin001' + // Non-required parameters + description: 'Collecting Linux-specific performance counters and Linux Syslog' + enableDefaultTelemetry: '' + kind: 'Linux' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + kind: 'Linux' + resourceType: 'Data Collection Rules' + } + } +} +``` + +
+

+ +

+ +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 + "dataFlows": { + "value": [ + { + "destinations": [ + "azureMonitorMetrics-default" + ], + "streams": [ + "Microsoft-InsightsMetrics" + ] + }, + { + "destinations": [ + "" + ], + "streams": [ + "Microsoft-Syslog" + ] + } + ] + }, + "dataSources": { + "value": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "Logical Disk(*)\\% Free Inodes", + "Logical Disk(*)\\% Free Space", + "Logical Disk(*)\\% Used Inodes", + "Logical Disk(*)\\% Used Space", + "Logical Disk(*)\\Disk Read Bytes/sec", + "Logical Disk(*)\\Disk Reads/sec", + "Logical Disk(*)\\Disk Transfers/sec", + "Logical Disk(*)\\Disk Write Bytes/sec", + "Logical Disk(*)\\Disk Writes/sec", + "Logical Disk(*)\\Free Megabytes", + "Logical Disk(*)\\Logical Disk Bytes/sec", + "Memory(*)\\% Available Memory", + "Memory(*)\\% Available Swap Space", + "Memory(*)\\% Used Memory", + "Memory(*)\\% Used Swap Space", + "Memory(*)\\Available MBytes Memory", + "Memory(*)\\Available MBytes Swap", + "Memory(*)\\Page Reads/sec", + "Memory(*)\\Page Writes/sec", + "Memory(*)\\Pages/sec", + "Memory(*)\\Used MBytes Swap Space", + "Memory(*)\\Used Memory MBytes", + "Network(*)\\Total Bytes", + "Network(*)\\Total Bytes Received", + "Network(*)\\Total Bytes Transmitted", + "Network(*)\\Total Collisions", + "Network(*)\\Total Packets Received", + "Network(*)\\Total Packets Transmitted", + "Network(*)\\Total Rx Errors", + "Network(*)\\Total Tx Errors", + "Processor(*)\\% DPC Time", + "Processor(*)\\% Idle Time", + "Processor(*)\\% Interrupt Time", + "Processor(*)\\% IO Wait Time", + "Processor(*)\\% Nice Time", + "Processor(*)\\% Privileged Time", + "Processor(*)\\% Processor Time", + "Processor(*)\\% User Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "syslog": [ + { + "facilityNames": [ + "auth", + "authpriv" + ], + "logLevels": [ + "Alert", + "Critical", + "Debug", + "Emergency", + "Error", + "Info", + "Notice", + "Warning" + ], + "name": "sysLogsDataSource-debugLevel", + "streams": [ + "Microsoft-Syslog" + ] + }, + { + "facilityNames": [ + "cron", + "daemon", + "kern", + "local0", + "mark" + ], + "logLevels": [ + "Alert", + "Critical", + "Emergency", + "Error", + "Warning" + ], + "name": "sysLogsDataSource-warningLevel", + "streams": [ + "Microsoft-Syslog" + ] + }, + { + "facilityNames": [ + "local1", + "local2", + "local3", + "local4", + "local5", + "local6", + "local7", + "lpr", + "mail", + "news", + "syslog" + ], + "logLevels": [ + "Alert", + "Critical", + "Emergency", + "Error" + ], + "name": "sysLogsDataSource-errLevel", + "streams": [ + "Microsoft-Syslog" + ] + } + ] + } + }, + "destinations": { + "value": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + } + }, + "name": { + "value": "<>idcrlin001" + }, + // Non-required parameters + "description": { + "value": "Collecting Linux-specific performance counters and Linux Syslog" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Linux" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "kind": "Linux", + "resourceType": "Data Collection Rules" + } + } + } +} +``` + +
+

+ +

Example 5: Min

+ +
+ +via Bicep module + +```bicep +module dataCollectionRules './Microsoft.Insights/dataCollectionRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcrmin' + params: { + // Required parameters + dataFlows: [ + { + destinations: [ + 'azureMonitorMetrics-default' + ] + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + '\\Process(_Total)\\Handle Count' + '\\Process(_Total)\\Thread Count' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Context Switches/sec' + '\\System\\Processes' + '\\System\\Processor Queue Length' + '\\System\\System Up Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + } + name: '<>idcrmin001' + // Non-required parameters + enableDefaultTelemetry: '' + kind: 'Windows' + } +} +``` + +
+

+ +

+ +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 + "dataFlows": { + "value": [ + { + "destinations": [ + "azureMonitorMetrics-default" + ], + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ] + }, + "dataSources": { + "value": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\Process(_Total)\\Handle Count", + "\\Process(_Total)\\Thread Count", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Context Switches/sec", + "\\System\\Processes", + "\\System\\Processor Queue Length", + "\\System\\System Up Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ] + } + }, + "destinations": { + "value": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + } + } + }, + "name": { + "value": "<>idcrmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Windows" + } + } +} +``` + +
+

+ +

Example 6: Windows

+ +
+ +via Bicep module + +```bicep +module dataCollectionRules './Microsoft.Insights/dataCollectionRules/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-idcrwin' + params: { + // Required parameters + dataFlows: [ + { + destinations: [ + 'azureMonitorMetrics-default' + ] + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + { + destinations: [ + '' + ] + streams: [ + 'Microsoft-Event' + ] + } + ] + dataSources: { + performanceCounters: [ + { + counterSpecifiers: [ + '\\LogicalDisk(_Total)\\% Disk Read Time' + '\\LogicalDisk(_Total)\\% Disk Time' + '\\LogicalDisk(_Total)\\% Disk Write Time' + '\\LogicalDisk(_Total)\\% Free Space' + '\\LogicalDisk(_Total)\\% Idle Time' + '\\LogicalDisk(_Total)\\Avg. Disk Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Read' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer' + '\\LogicalDisk(_Total)\\Avg. Disk sec/Write' + '\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length' + '\\LogicalDisk(_Total)\\Disk Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Read Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Reads/sec' + '\\LogicalDisk(_Total)\\Disk Transfers/sec' + '\\LogicalDisk(_Total)\\Disk Write Bytes/sec' + '\\LogicalDisk(_Total)\\Disk Writes/sec' + '\\LogicalDisk(_Total)\\Free Megabytes' + '\\Memory\\% Committed Bytes In Use' + '\\Memory\\Available Bytes' + '\\Memory\\Cache Bytes' + '\\Memory\\Committed Bytes' + '\\Memory\\Page Faults/sec' + '\\Memory\\Pages/sec' + '\\Memory\\Pool Nonpaged Bytes' + '\\Memory\\Pool Paged Bytes' + '\\Network Interface(*)\\Bytes Received/sec' + '\\Network Interface(*)\\Bytes Sent/sec' + '\\Network Interface(*)\\Bytes Total/sec' + '\\Network Interface(*)\\Packets Outbound Errors' + '\\Network Interface(*)\\Packets Received Errors' + '\\Network Interface(*)\\Packets Received/sec' + '\\Network Interface(*)\\Packets Sent/sec' + '\\Network Interface(*)\\Packets/sec' + '\\Process(_Total)\\Handle Count' + '\\Process(_Total)\\Thread Count' + '\\Process(_Total)\\Working Set' + '\\Process(_Total)\\Working Set - Private' + '\\Processor Information(_Total)\\% Privileged Time' + '\\Processor Information(_Total)\\% Processor Time' + '\\Processor Information(_Total)\\% User Time' + '\\Processor Information(_Total)\\Processor Frequency' + '\\System\\Context Switches/sec' + '\\System\\Processes' + '\\System\\Processor Queue Length' + '\\System\\System Up Time' + ] + name: 'perfCounterDataSource60' + samplingFrequencyInSeconds: 60 + streams: [ + 'Microsoft-InsightsMetrics' + ] + } + ] + windowsEventLogs: [ + { + name: 'eventLogsDataSource' + streams: [ + 'Microsoft-Event' + ] + xPathQueries: [ + 'Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + 'Security!*[System[(band(Keywords13510798882111488))]]' + 'System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]' + ] + } + ] + } + destinations: { + azureMonitorMetrics: { + name: 'azureMonitorMetrics-default' + } + logAnalytics: [ + { + name: '' + workspaceResourceId: '' + } + ] + } + name: '<>idcrwin001' + // Non-required parameters + description: 'Collecting Windows-specific performance counters and Windows Event Logs' + enableDefaultTelemetry: '' + kind: 'Windows' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + kind: 'Windows' + resourceType: 'Data Collection Rules' + } + } +} +``` + +
+

+ +

+ +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 + "dataFlows": { + "value": [ + { + "destinations": [ + "azureMonitorMetrics-default" + ], + "streams": [ + "Microsoft-InsightsMetrics" + ] + }, + { + "destinations": [ + "" + ], + "streams": [ + "Microsoft-Event" + ] + } + ] + }, + "dataSources": { + "value": { + "performanceCounters": [ + { + "counterSpecifiers": [ + "\\LogicalDisk(_Total)\\% Disk Read Time", + "\\LogicalDisk(_Total)\\% Disk Time", + "\\LogicalDisk(_Total)\\% Disk Write Time", + "\\LogicalDisk(_Total)\\% Free Space", + "\\LogicalDisk(_Total)\\% Idle Time", + "\\LogicalDisk(_Total)\\Avg. Disk Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk Read Queue Length", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Read", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Transfer", + "\\LogicalDisk(_Total)\\Avg. Disk sec/Write", + "\\LogicalDisk(_Total)\\Avg. Disk Write Queue Length", + "\\LogicalDisk(_Total)\\Disk Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Read Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Reads/sec", + "\\LogicalDisk(_Total)\\Disk Transfers/sec", + "\\LogicalDisk(_Total)\\Disk Write Bytes/sec", + "\\LogicalDisk(_Total)\\Disk Writes/sec", + "\\LogicalDisk(_Total)\\Free Megabytes", + "\\Memory\\% Committed Bytes In Use", + "\\Memory\\Available Bytes", + "\\Memory\\Cache Bytes", + "\\Memory\\Committed Bytes", + "\\Memory\\Page Faults/sec", + "\\Memory\\Pages/sec", + "\\Memory\\Pool Nonpaged Bytes", + "\\Memory\\Pool Paged Bytes", + "\\Network Interface(*)\\Bytes Received/sec", + "\\Network Interface(*)\\Bytes Sent/sec", + "\\Network Interface(*)\\Bytes Total/sec", + "\\Network Interface(*)\\Packets Outbound Errors", + "\\Network Interface(*)\\Packets Received Errors", + "\\Network Interface(*)\\Packets Received/sec", + "\\Network Interface(*)\\Packets Sent/sec", + "\\Network Interface(*)\\Packets/sec", + "\\Process(_Total)\\Handle Count", + "\\Process(_Total)\\Thread Count", + "\\Process(_Total)\\Working Set", + "\\Process(_Total)\\Working Set - Private", + "\\Processor Information(_Total)\\% Privileged Time", + "\\Processor Information(_Total)\\% Processor Time", + "\\Processor Information(_Total)\\% User Time", + "\\Processor Information(_Total)\\Processor Frequency", + "\\System\\Context Switches/sec", + "\\System\\Processes", + "\\System\\Processor Queue Length", + "\\System\\System Up Time" + ], + "name": "perfCounterDataSource60", + "samplingFrequencyInSeconds": 60, + "streams": [ + "Microsoft-InsightsMetrics" + ] + } + ], + "windowsEventLogs": [ + { + "name": "eventLogsDataSource", + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Application!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]", + "Security!*[System[(band(Keywords,13510798882111488))]]", + "System!*[System[(Level=1 or Level=2 or Level=3 or Level=4 or Level=0 or Level=5)]]" + ] + } + ] + } + }, + "destinations": { + "value": { + "azureMonitorMetrics": { + "name": "azureMonitorMetrics-default" + }, + "logAnalytics": [ + { + "name": "", + "workspaceResourceId": "" + } + ] + } + }, + "name": { + "value": "<>idcrwin001" + }, + // Non-required parameters + "description": { + "value": "Collecting Windows-specific performance counters and Windows Event Logs" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "Windows" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "kind": "Windows", + "resourceType": "Data Collection Rules" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/dataCollectionRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/.test/common/deploy.test.bicep new file mode 100644 index 000000000..110ff98e6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/.test/common/deploy.test.bicep @@ -0,0 +1,60 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.diagnosticsettings-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'idscom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/deploy.bicep new file mode 100644 index 000000000..0a524edea --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/deploy.bicep @@ -0,0 +1,98 @@ +targetScope = 'subscription' + +@description('Optional. Name of the ActivityLog diagnostic settings.') +@minLength(1) +@maxLength(260) +param name string = '${uniqueString(subscription().id)}-ActivityLog' + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'Administrative' + 'Security' + 'ServiceHealth' + 'Alert' + 'Recommendation' + 'Policy' + 'Autoscale' + 'ResourceHealth' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: name + properties: { + storageAccountId: (empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId) + workspaceId: (empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId) + eventHubAuthorizationRuleId: (empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId) + eventHubName: (empty(diagnosticEventHubName) ? null : diagnosticEventHubName) + logs: ((empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName)) ? null : diagnosticsLogs) + } +} + +@description('The name of the diagnostic settings.') +output name string = diagnosticSetting.name + +@description('The resource ID of the diagnostic settings.') +output resourceId string = diagnosticSetting.id + +@description('The name of the subscription to deploy into.') +output subscriptionName string = subscription().displayName diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/readme.md new file mode 100644 index 000000000..bbbdcbaba --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/readme.md @@ -0,0 +1,114 @@ +# Activity Logs `[Microsoft.Insights/diagnosticSettings]` + +This module deploys a subscription wide export of the activity log. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Parameters + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[Administrative, Alert, allLogs, Autoscale, Policy, Recommendation, ResourceHealth, Security, ServiceHealth]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location deployment metadata. | +| `name` | string | `[format('{0}-ActivityLog', uniqueString(subscription().id))]` | | Name of the ActivityLog diagnostic settings. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the diagnostic settings. | +| `resourceId` | string | The resource ID of the diagnostic settings. | +| `subscriptionName` | string | The name of the subscription to deploy into. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module diagnosticSettings './Microsoft.Insights/diagnosticSettings/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-idscom' + params: { + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + name: '<>idscom001' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "name": { + "value": "<>idscom001" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/diagnosticSettings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..ad04f79f7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource metricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(metricAlert.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: metricAlert +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/dependencies.bicep new file mode 100644 index 000000000..eb23eca83 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/dependencies.bicep @@ -0,0 +1,29 @@ +@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 Action Group to create.') +param actionGroupName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = { + name: actionGroupName + location: 'global' + + properties: { + enabled: true + groupShortName: substring(actionGroupName, 0, 11) + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Action Group.') +output actionGroupResourceId string = actionGroup.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/deploy.test.bicep new file mode 100644 index 000000000..687c774c5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/.test/common/deploy.test.bicep @@ -0,0 +1,82 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.metricalerts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'imacom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + actionGroupName: 'dep-<>-ag-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + criterias: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } + ] + actions: [ + nestedDependencies.outputs.actionGroupResourceId + ] + alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + targetResourceRegion: 'westeurope' + targetResourceType: 'microsoft.compute/virtualmachines' + windowSize: 'PT15M' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep new file mode 100644 index 000000000..a09011989 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep @@ -0,0 +1,145 @@ +@description('Required. The name of the alert.') +param name string + +@description('Optional. Description of the alert.') +param alertDescription string = '' + +@description('Optional. Location for all resources.') +param location string = 'global' + +@description('Optional. Indicates whether this alert is enabled.') +param enabled bool = true + +@description('Optional. The severity of the alert.') +@allowed([ + 0 + 1 + 2 + 3 + 4 +]) +param severity int = 3 + +@description('Optional. how often the metric alert is evaluated represented in ISO 8601 duration format.') +@allowed([ + 'PT1M' + 'PT5M' + 'PT15M' + 'PT30M' + 'PT1H' +]) +param evaluationFrequency string = 'PT5M' + +@description('Optional. the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold.') +@allowed([ + 'PT1M' + 'PT5M' + 'PT15M' + 'PT30M' + 'PT1H' + 'PT6H' + 'PT12H' + 'P1D' +]) +param windowSize string = 'PT15M' + +@description('Optional. the list of resource IDs that this metric alert is scoped to.') +param scopes array = [ + subscription().id +] + +@description('Conditional. The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria.') +param targetResourceType string = '' + +@description('Conditional. The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria.') +param targetResourceRegion string = '' + +@description('Optional. The flag that indicates whether the alert should be auto resolved or not.') +param autoMitigate bool = true + +@description('Optional. The list of actions to take when alert triggers.') +param actions array = [] + +@description('Optional. Maps to the \'odata.type\' field. Specifies the type of the alert criteria.') +@allowed([ + 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + 'Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria' +]) +param alertCriteriaType string = 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + +@description('Required. Criterias to trigger the alert. Array of \'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria\' or \'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria\' objects.') +param criterias array + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var actionGroups = [for action in actions: { + actionGroupId: contains(action, 'actionGroupId') ? action.actionGroupId : action + webHookProperties: contains(action, 'webHookProperties') ? action.webHookProperties : null +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource metricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = { + name: name + location: location + tags: tags + properties: { + description: alertDescription + severity: severity + enabled: enabled + scopes: scopes + evaluationFrequency: evaluationFrequency + windowSize: windowSize + targetResourceType: targetResourceType + targetResourceRegion: targetResourceRegion + criteria: { + 'odata.type': any(alertCriteriaType) + allOf: criterias + } + autoMitigate: autoMitigate + actions: actionGroups + } +} + +module metricAlert_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-MetricAlert-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: metricAlert.id + } +}] + +@description('The resource group the metric alert was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the metric alert.') +output name string = metricAlert.name + +@description('The resource ID of the metric alert.') +output resourceId string = metricAlert.id + +@description('The location the resource was deployed into.') +output location string = metricAlert.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/readme.md new file mode 100644 index 000000000..e8f90b339 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/readme.md @@ -0,0 +1,512 @@ +# Metric Alerts `[Microsoft.Insights/metricAlerts]` + +This module deploys an alert based on metrics. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Insights/metricAlerts` | [2018-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2018-03-01/metricAlerts) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `criterias` | array | Criterias to trigger the alert. Array of 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' or 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' objects. | +| `name` | string | The name of the alert. | + +**Conditional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `targetResourceRegion` | string | `''` | The region of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria. | +| `targetResourceType` | string | `''` | The resource type of the target resource(s) on which the alert is created/updated. Required if alertCriteriaType is MultipleResourceMultipleMetricCriteria. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | | The list of actions to take when alert triggers. | +| `alertCriteriaType` | string | `'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'` | `[Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria, Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria, Microsoft.Azure.Monitor.WebtestLocationAvailabilityCriteria]` | Maps to the 'odata.type' field. Specifies the type of the alert criteria. | +| `alertDescription` | string | `''` | | Description of the alert. | +| `autoMitigate` | bool | `True` | | The flag that indicates whether the alert should be auto resolved or not. | +| `enabled` | bool | `True` | | Indicates whether this alert is enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `evaluationFrequency` | string | `'PT5M'` | `[PT15M, PT1H, PT1M, PT30M, PT5M]` | how often the metric alert is evaluated represented in ISO 8601 duration format. | +| `location` | string | `'global'` | | Location for all resources. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `scopes` | array | `[[subscription().id]]` | | the list of resource IDs that this metric alert is scoped to. | +| `severity` | int | `3` | `[0, 1, 2, 3, 4]` | The severity of the alert. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `windowSize` | string | `'PT15M'` | `[P1D, PT12H, PT15M, PT1H, PT1M, PT30M, PT5M, PT6H]` | the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold. | + + +### Parameter Usage: actions + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + { + "actionGroupId": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/ActionGroupName", + "webhookProperties": {} + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +actions: [ + { + actionGroupId: '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/ActionGroupName' + webhookProperties: {} + } +] +``` + +
+

+ +`webhookProperties` is optional. + +If you do only want to provide actionGroupIds, a shorthand use of the parameter is available. + +

+ +Parameter JSON format + +```json +"actions": { + "value": [ + "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgName/providers/microsoft.insights/actiongroups/actionGroupName" + ] +} +``` + +
+ + +
+ +Bicep format + +```bicep + + +``` + +
+ +### Parameter Usage: `criteria` + +**SingleResourceMultipleMetricCriteria** + + +
+ +Parameter JSON format + +```json +{ + "criterionType": "string", + "dimensions": [], + "metricName": "string", + "metricNamespace": "string", + "name": "string", + "operator": "string", + "threshold": "integer", + "timeAggregation": "string" +} +``` + +
+ + +
+ +Bicep format + +```bicep +{ + criterionType: 'string' + dimensions: [] + metricName: 'string' + metricNamespace: 'string' + name: 'string' + operator: 'string' + threshold: 'integer' + timeAggregation: 'string' +} +``` + +
+

+ +**MultipleResourceMultipleMetricCriteria** + +

+ +Parameter JSON format + +```json +{ + "criterionType": "string", + "dimensions": [], + "metricName": "string", + "metricNamespace": "string", + "name": "string", + "operator": "string", + "threshold": "integer", + "timeAggregation": "string", + "alertSensitivity": "string", + "failingPeriods": { + "minFailingPeriodsToAlert": "integer", + "numberOfEvaluationPeriods": "integer" + }, + "ignoreDataBefore": "string" +} +``` + +
+ + +
+ +Bicep format + +```bicep +{ + criterionType: 'string' + dimensions: [] + metricName: 'string' + metricNamespace: 'string' + name: 'string' + operator: 'string' + threshold: 'integer' + timeAggregation: 'string' + alertSensitivity: 'string' + failingPeriods: { + minFailingPeriodsToAlert: 'integer' + numberOfEvaluationPeriods: 'integer' + } + ignoreDataBefore: 'string' +} +``` + +
+

+ +**Sample** +The following sample can be use both for Single and Multiple criteria. The other parameters are optional. + +

+ +Parameter JSON format + +```json +"criterias":{ + "value": [ + { + "criterionType": "StaticThresholdCriterion", + "metricName": "Percentage CPU", + "metricNamespace": "microsoft.compute/virtualmachines", + "name": "HighCPU", + "operator": "GreaterThan", + "threshold": "90", + "timeAggregation": "Average" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +criterias: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Additional notes on parameters + +- When using MultipleResourceMultipleMetricCriteria criteria type, some parameters becomes mandatory (see above) +- MultipleResourceMultipleMetricCriteria is suggested, as additional scopes can be added later +- It's not possible to convert from SingleResourceMultipleMetricCriteria to MultipleResourceMultipleMetricCriteria. Delete and re-create the alert. + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the metric alert. | +| `resourceGroupName` | string | The resource group the metric alert was deployed into. | +| `resourceId` | string | The resource ID of the metric alert. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module metricAlerts './Microsoft.Insights/metricAlerts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-imacom' + params: { + // Required parameters + criterias: [ + { + criterionType: 'StaticThresholdCriterion' + metricName: 'Percentage CPU' + metricNamespace: 'microsoft.compute/virtualmachines' + name: 'HighCPU' + operator: 'GreaterThan' + threshold: '90' + timeAggregation: 'Average' + } + ] + name: '<>imacom001' + // Non-required parameters + actions: [ + '' + ] + alertCriteriaType: 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + enableDefaultTelemetry: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + targetResourceRegion: 'westeurope' + targetResourceType: 'microsoft.compute/virtualmachines' + windowSize: 'PT15M' + } +} +``` + +
+

+ +

+ +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 + "criterias": { + "value": [ + { + "criterionType": "StaticThresholdCriterion", + "metricName": "Percentage CPU", + "metricNamespace": "microsoft.compute/virtualmachines", + "name": "HighCPU", + "operator": "GreaterThan", + "threshold": "90", + "timeAggregation": "Average" + } + ] + }, + "name": { + "value": "<>imacom001" + }, + // Non-required parameters + "actions": { + "value": [ + "" + ] + }, + "alertCriteriaType": { + "value": "Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "targetResourceRegion": { + "value": "westeurope" + }, + "targetResourceType": { + "value": "microsoft.compute/virtualmachines" + }, + "windowSize": { + "value": "PT15M" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/metricAlerts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..2825eb379 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource privateLinkScope 'Microsoft.Insights/privateLinkScopes@2019-10-17-preview' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateLinkScope.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateLinkScope +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/dependencies.bicep new file mode 100644 index 000000000..3b81287ac --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/dependencies.bicep @@ -0,0 +1,71 @@ +@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 Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.monitor.azure.com' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: logAnalyticsWorkspaceName + location: location +} + +@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 Private DNS Zone.') +output privateDNSResourceId string = privateDNSZone.id + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/deploy.test.bicep new file mode 100644 index 000000000..e27079ea3 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/common/deploy.test.bicep @@ -0,0 +1,86 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.privatelinkscopes-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iplscom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-la-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + scopedResources: [ + { + name: 'scoped1' + linkedResourceId: nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + nestedDependencies.outputs.privateDNSResourceId + ] + } + service: 'azuremonitor' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/min/deploy.test.bicep new file mode 100644 index 000000000..58fb7f3ab --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.privatelinkscopes-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iplsmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/deploy.bicep new file mode 100644 index 000000000..1ec84c0a4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/deploy.bicep @@ -0,0 +1,117 @@ +@description('Required. Name of the private link scope.') +@minLength(1) +param name string + +@description('Optional. The location of the private link scope. Should be global.') +param location string = 'global' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Configuration details for Azure Monitor Resources.') +param scopedResources array = [] + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkScope 'Microsoft.Insights/privateLinkScopes@2019-10-17-preview' = { + name: name + location: location + tags: tags + properties: {} +} + +module privateLinkScope_scopedResource 'scopedResources/deploy.bicep' = [for (scopedResource, index) in scopedResources: { + name: '${uniqueString(deployment().name, location)}-PvtLinkScope-ScopedRes-${index}' + params: { + name: scopedResource.name + privateLinkScopeName: privateLinkScope.name + linkedResourceId: scopedResource.linkedResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource privateLinkScope_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${privateLinkScope.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateLinkScope +} + +module privateLinkScope_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-PvtLinkScope-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(privateLinkScope.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: privateLinkScope.id + subnetResourceId: privateEndpoint.subnetResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroup: contains(privateEndpoint, 'privateDnsZoneGroup') ? privateEndpoint.privateDnsZoneGroup : {} + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + ipConfigurations: contains(privateEndpoint, 'ipConfigurations') ? privateEndpoint.ipConfigurations : [] + applicationSecurityGroups: contains(privateEndpoint, 'applicationSecurityGroups') ? privateEndpoint.applicationSecurityGroups : [] + customNetworkInterfaceName: contains(privateEndpoint, 'customNetworkInterfaceName') ? privateEndpoint.customNetworkInterfaceName : '' + } +}] + +module privateLinkScope_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PvtLinkScope-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateLinkScope.id + } +}] + +@description('The name of the private link scope.') +output name string = privateLinkScope.name + +@description('The resource ID of the private link scope.') +output resourceId string = privateLinkScope.id + +@description('The resource group the private link scope was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = privateLinkScope.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/readme.md new file mode 100644 index 000000000..902ce74c1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/readme.md @@ -0,0 +1,433 @@ +# Azure Monitor Private Link Scopes `[Microsoft.Insights/privateLinkScopes]` + +This module deploys an Azure Monitor Private Link Scope. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/privateLinkScopes` | [2019-10-17-preview](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2019-10-17-preview/privateLinkScopes) | +| `Microsoft.Insights/privateLinkScopes/scopedResources` | [2021-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-07-01-preview/privateLinkScopes/scopedResources) | +| `Microsoft.Network/privateEndpoints` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the private link scope. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `'global'` | | The location of the private link scope. Should be global. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `scopedResources` | _[scopedResources](scopedResources/readme.md)_ array | `[]` | | Configuration details for Azure Monitor Resources. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `privateEndpoints` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +

+ +Parameter JSON format + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "", // e.g. vault, registry, blob + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/" // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + }, + "ipConfigurations":[ + { + "name": "myIPconfigTest02", + "properties": { + "groupId": "blob", + "memberName": "blob", + "privateIPAddress": "10.0.0.30" + } + } + ], + "customDnsConfigs": [ + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "" // e.g. vault, registry, blob + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // Example showing all available fields + { + name: 'sxx-az-pe' // Optional: Name will be automatically generated if one is not provided here + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + privateDnsZoneGroup: { + privateDNSResourceIds: [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/' // e.g. privatelink.vaultcore.azure.net, privatelink.azurecr.io, privatelink.blob.core.windows.net + ] + } + customDnsConfigs: [ + { + fqdn: 'customname.test.local' + ipAddresses: [ + '10.10.10.10' + ] + } + ] + ipConfigurations:[ + { + name: 'myIPconfigTest02' + properties: { + groupId: 'blob' + memberName: 'blob' + privateIPAddress: '10.0.0.30' + } + } + ] + } + // Example showing only mandatory fields + { + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '' // e.g. vault, registry, blob + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private link scope. | +| `resourceGroupName` | string | The resource group the private link scope was deployed into. | +| `resourceId` | string | The resource ID of the private link scope. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module privateLinkScopes './Microsoft.Insights/privateLinkScopes/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iplscom' + params: { + // Required parameters + name: '<>iplscom001' + // Non-required parameters + enableDefaultTelemetry: '' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '' + ] + } + service: 'azuremonitor' + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + scopedResources: [ + { + linkedResourceId: '' + name: 'scoped1' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>iplscom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDNSResourceIds": [ + "" + ] + }, + "service": "azuremonitor", + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scopedResources": { + "value": [ + { + "linkedResourceId": "", + "name": "scoped1" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module privateLinkScopes './Microsoft.Insights/privateLinkScopes/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iplsmin' + params: { + // Required parameters + name: '<>iplsmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>iplsmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep new file mode 100644 index 000000000..c1d7664b5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/deploy.bicep @@ -0,0 +1,46 @@ +@description('Required. Name of the private link scoped resource.') +@minLength(1) +param name string + +@description('Conditional. The name of the parent private link scope. Required if the template is used in a standalone deployment.') +@minLength(1) +param privateLinkScopeName string + +@description('Required. The resource ID of the scoped Azure monitor resource.') +param linkedResourceId string + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkScope 'microsoft.insights/privateLinkScopes@2021-07-01-preview' existing = { + name: privateLinkScopeName +} + +resource scopedResource 'Microsoft.Insights/privateLinkScopes/scopedResources@2021-07-01-preview' = { + name: name + parent: privateLinkScope + properties: { + linkedResourceId: linkedResourceId + } +} + +@description('The name of the resource group where the resource has been deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the deployed scopedResource.') +output resourceId string = scopedResource.id + +@description('The full name of the deployed Scoped Resource.') +output name string = scopedResource.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md new file mode 100644 index 000000000..2f0221fe6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/readme.md @@ -0,0 +1,50 @@ +# Insights PrivateLinkScopes ScopedResources `[Microsoft.Insights/privateLinkScopes/scopedResources]` + +This module deploys Insights PrivateLinkScopes ScopedResources. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Insights/privateLinkScopes/scopedResources` | [2021-07-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-07-01-preview/privateLinkScopes/scopedResources) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `linkedResourceId` | string | The resource ID of the scoped Azure monitor resource. | +| `name` | string | Name of the private link scoped resource. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateLinkScopeName` | string | The name of the parent private link scope. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The full name of the deployed Scoped Resource. | +| `resourceGroupName` | string | The name of the resource group where the resource has been deployed. | +| `resourceId` | string | The resource ID of the deployed scopedResource. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/scopedResources/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/privateLinkScopes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..07285245c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource queryAlert 'microsoft.insights/scheduledQueryRules@2018-04-16' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(queryAlert.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: queryAlert +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/dependencies.bicep new file mode 100644 index 000000000..9e9a8f251 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/dependencies.bicep @@ -0,0 +1,24 @@ +@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 Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/deploy.test.bicep new file mode 100644 index 000000000..43996e393 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/.test/common/deploy.test.bicep @@ -0,0 +1,100 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.scheduledqueryrules-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'isqrcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + alertDescription: 'My sample Alert' + autoMitigate: false + criterias: { + allOf: [ + { + dimensions: [ + { + name: 'Computer' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'InstanceName' + operator: 'Include' + values: [ + '*' + ] + } + ] + metricMeasureColumn: 'AggregatedValue' + operator: 'GreaterThan' + query: 'Perf | where ObjectName == "LogicalDisk" | where CounterName == "% Free Space" | where InstanceName <> "HarddiskVolume1" and InstanceName <> "_Total" | summarize AggregatedValue = min(CounterValue) by Computer, InstanceName, bin(TimeGenerated,5m)' + threshold: 0 + timeAggregation: 'Average' + } + ] + } + evaluationFrequency: 'PT5M' + queryTimeRange: 'PT5M' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + scopes: [ + nestedDependencies.outputs.logAnalyticsWorkspaceResourceId + ] + suppressForMinutes: 'PT5M' + windowSize: 'PT5M' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/deploy.bicep new file mode 100644 index 000000000..3266f2f72 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/deploy.bicep @@ -0,0 +1,133 @@ +@description('Required. The name of the Alert.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The display name of the scheduled query rule.') +param alertDisplayName string = '' + +@description('Optional. The description of the scheduled query rule.') +param alertDescription string = '' + +@description('Optional. The flag which indicates whether this scheduled query rule is enabled.') +param enabled bool = true + +@description('Optional. Indicates the type of scheduled query rule.') +@allowed([ + 'LogAlert' + 'LogToMetric' +]) +param kind string = 'LogAlert' + +@description('Optional. The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert.') +param autoMitigate bool = true + +@description('Optional. If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert.') +param queryTimeRange string = '' + +@description('Optional. The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert.') +param skipQueryValidation bool = false + +@description('Optional. List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert.') +param targetResourceTypes array = [] + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Required. The list of resource IDs that this scheduled query rule is scoped to.') +param scopes array + +@description('Optional. Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert.') +@allowed([ + 0 + 1 + 2 + 3 + 4 +]) +param severity int = 3 + +@description('Optional. How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert.') +param evaluationFrequency string = '' + +@description('Optional. The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert.') +param windowSize string = '' + +@description('Optional. Actions to invoke when the alert fires.') +param actions array = [] + +@description('Required. The rule criteria that defines the conditions of the scheduled query rule.') +param criterias object + +@description('Optional. Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert.') +param suppressForMinutes string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource queryRule 'Microsoft.Insights/scheduledQueryRules@2021-02-01-preview' = { + name: name + location: location + tags: tags + kind: kind + properties: { + actions: { + actionGroups: actions + customProperties: {} + } + autoMitigate: (kind == 'LogAlert') ? autoMitigate : null + criteria: criterias + description: alertDescription + displayName: !empty(alertDisplayName) ? alertDisplayName : name + enabled: enabled + evaluationFrequency: (kind == 'LogAlert' && !empty(evaluationFrequency)) ? evaluationFrequency : null + muteActionsDuration: (kind == 'LogAlert' && !empty(suppressForMinutes)) ? suppressForMinutes : null + overrideQueryTimeRange: (kind == 'LogAlert' && !empty(queryTimeRange)) ? queryTimeRange : null + scopes: scopes + severity: (kind == 'LogAlert') ? severity : null + skipQueryValidation: (kind == 'LogAlert') ? skipQueryValidation : null + targetResourceTypes: (kind == 'LogAlert') ? targetResourceTypes : null + windowSize: (kind == 'LogAlert' && !empty(windowSize)) ? windowSize : null + } +} + +module queryRule_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-QueryRule-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: queryRule.id + } +}] + +@description('The Name of the created query rule.') +output name string = queryRule.name + +@description('The resource ID of the created query rule.') +output resourceId string = queryRule.id + +@description('The Resource Group of the created query rule.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = queryRule.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/readme.md new file mode 100644 index 000000000..7642ef048 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/readme.md @@ -0,0 +1,338 @@ +# Scheduled Query Rules `[Microsoft.Insights/scheduledQueryRules]` + +This module deploys a scheduled query rule. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Insights/scheduledQueryRules` | [2021-02-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-02-01-preview/scheduledQueryRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `criterias` | object | The rule criteria that defines the conditions of the scheduled query rule. | +| `name` | string | The name of the Alert. | +| `scopes` | array | The list of resource IDs that this scheduled query rule is scoped to. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `actions` | array | `[]` | | Actions to invoke when the alert fires. | +| `alertDisplayName` | string | `''` | | The display name of the scheduled query rule. | +| `alertDescription` | string | `''` | | The description of the scheduled query rule. | +| `autoMitigate` | bool | `True` | | The flag that indicates whether the alert should be automatically resolved or not. Relevant only for rules of the kind LogAlert. | +| `enabled` | bool | `True` | | The flag which indicates whether this scheduled query rule is enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `evaluationFrequency` | string | `''` | | How often the scheduled query rule is evaluated represented in ISO 8601 duration format. Relevant and required only for rules of the kind LogAlert. | +| `kind` | string | `'LogAlert'` | `[LogAlert, LogToMetric]` | Indicates the type of scheduled query rule. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `queryTimeRange` | string | `''` | | If specified (in ISO 8601 duration format) then overrides the query time range. Relevant only for rules of the kind LogAlert. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `severity` | int | `3` | `[0, 1, 2, 3, 4]` | Severity of the alert. Should be an integer between [0-4]. Value of 0 is severest. Relevant and required only for rules of the kind LogAlert. | +| `skipQueryValidation` | bool | `False` | | The flag which indicates whether the provided query should be validated or not. Relevant only for rules of the kind LogAlert. | +| `suppressForMinutes` | string | `''` | | Mute actions for the chosen period of time (in ISO 8601 duration format) after the alert is fired. If set, autoMitigate must be disabled.Relevant only for rules of the kind LogAlert. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `targetResourceTypes` | array | `[]` | | List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is Microsoft.Compute/virtualMachines, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. Relevant only for rules of the kind LogAlert. | +| `windowSize` | string | `''` | | The period of time (in ISO 8601 duration format) on which the Alert query will be executed (bin size). Relevant and required only for rules of the kind LogAlert. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Name of the created query rule. | +| `resourceGroupName` | string | The Resource Group of the created query rule. | +| `resourceId` | string | The resource ID of the created query rule. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module scheduledQueryRules './Microsoft.Insights/scheduledQueryRules/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-isqrcom' + params: { + // Required parameters + criterias: { + allOf: [ + { + dimensions: [ + { + name: 'Computer' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'InstanceName' + operator: 'Include' + values: [ + '*' + ] + } + ] + metricMeasureColumn: 'AggregatedValue' + operator: 'GreaterThan' + query: 'Perf | where ObjectName == \'LogicalDisk\' | where CounterName == \'% Free Space\' | where InstanceName <> \'HarddiskVolume1\' and InstanceName <> \'_Total\' | summarize AggregatedValue = min(CounterValue) by Computer InstanceName bin(TimeGenerated5m)' + threshold: 0 + timeAggregation: 'Average' + } + ] + } + name: '<>isqrcom001' + scopes: [ + '' + ] + // Non-required parameters + alertDescription: 'My sample Alert' + alertDisplayName: 'My alert friendly name' + autoMitigate: false + enableDefaultTelemetry: '' + evaluationFrequency: 'PT5M' + queryTimeRange: 'PT5M' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + suppressForMinutes: 'PT5M' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + windowSize: 'PT5M' + } +} +``` + +
+

+ +

+ +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 + "criterias": { + "value": { + "allOf": [ + { + "dimensions": [ + { + "name": "Computer", + "operator": "Include", + "values": [ + "*" + ] + }, + { + "name": "InstanceName", + "operator": "Include", + "values": [ + "*" + ] + } + ], + "metricMeasureColumn": "AggregatedValue", + "operator": "GreaterThan", + "query": "Perf | where ObjectName == \"LogicalDisk\" | where CounterName == \"% Free Space\" | where InstanceName <> \"HarddiskVolume1\" and InstanceName <> \"_Total\" | summarize AggregatedValue = min(CounterValue) by Computer, InstanceName, bin(TimeGenerated,5m)", + "threshold": 0, + "timeAggregation": "Average" + } + ] + } + }, + "name": { + "value": "<>isqrcom001" + }, + "scopes": { + "value": [ + "" + ] + }, + // Non-required parameters + "alertDescription": { + "value": "My sample Alert" + }, + "alertDisplayName": { + "value": "My alert friendly name" + }, + "autoMitigate": { + "value": false + }, + "enableDefaultTelemetry": { + "value": "" + }, + "evaluationFrequency": { + "value": "PT5M" + }, + "queryTimeRange": { + "value": "PT5M" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "suppressForMinutes": { + "value": "PT5M" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "windowSize": { + "value": "PT5M" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/scheduledQueryRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..db6798b5e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,198 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'Disk Pool Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + 'Workbook Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e8ddcd69-c73f-4f9f-9844-4100522f16ad') + 'Workbook Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b279062a-9be3-42a0-92ae-8b3cf002ec4d') +} + +resource webtest 'Microsoft.Insights/webtests@2022-06-15' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(webtest.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: webtest +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/dependencies.bicep new file mode 100644 index 000000000..79e003515 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/dependencies.bicep @@ -0,0 +1,26 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param appInsightName string + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +resource appInsight 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +@description('The resource ID of the created Log Analytics Workspace.') +output appInsightResourceId string = appInsight.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/deploy.test.bicep new file mode 100644 index 000000000..e12d8d3ba --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/common/deploy.test.bicep @@ -0,0 +1,67 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.webtests-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iwtcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + appInsightName: 'dep-<>-appi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + tags: { + 'hidden-link:${nestedDependencies.outputs.appInsightResourceId}': 'Resource' + } + enableDefaultTelemetry: enableDefaultTelemetry + webTestName: 'wt<>$${serviceShort}001' + syntheticMonitorId: '<>${serviceShort}001' + locations: [ + { + Id: 'emea-nl-ams-azr' + } + ] + request: { + RequestUrl: 'https://learn.microsoft.com/en-us/' + HttpVerb: 'GET' + } + lock: 'CanNotDelete' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/dependencies.bicep new file mode 100644 index 000000000..79e003515 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/dependencies.bicep @@ -0,0 +1,26 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param appInsightName string + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +resource appInsight 'Microsoft.Insights/components@2020-02-02' = { + name: appInsightName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +@description('The resource ID of the created Log Analytics Workspace.') +output appInsightResourceId string = appInsight.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/deploy.test.bicep new file mode 100644 index 000000000..466ef4c2a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/.test/min/deploy.test.bicep @@ -0,0 +1,60 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.insights.webtests-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'iwtmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + appInsightName: 'dep-<>-appi-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: '<>${serviceShort}001' + tags: { + 'hidden-link:${nestedDependencies.outputs.appInsightResourceId}': 'Resource' + } + enableDefaultTelemetry: enableDefaultTelemetry + webTestName: 'wt<>$${serviceShort}001' + request: { + RequestUrl: 'https://learn.microsoft.com/en-us/' + HttpVerb: 'GET' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/deploy.bicep new file mode 100644 index 000000000..dcbf14e11 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/deploy.bicep @@ -0,0 +1,145 @@ +@sys.description('Required. Name of the webtest.') +param name string + +@sys.description('Required. User defined name if this WebTest.') +param webTestName string + +@sys.description('Required. A single hidden-link tag pointing to an existing AI component is required.') +param tags object + +@sys.description('Required. The collection of request properties.') +param request object + +@sys.description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@sys.description('Optional. User defined description for this WebTest.') +param description string = '' + +@sys.description('Optional. Unique ID of this WebTest.') +param syntheticMonitorId string = name + +@sys.description('Optional. The kind of WebTest that this web test watches.') +@allowed([ + 'multistep' + 'ping' + 'standard' +]) +param kind string = 'standard' + +@sys.description('Optional. List of where to physically run the tests from to give global coverage for accessibility of your application.') +param locations array = [ + { + Id: 'us-il-ch1-azr' + } + { + Id: 'us-fl-mia-edge' + } + { + Id: 'latam-br-gru-edge' + } + { + Id: 'apac-sg-sin-azr' + } + { + Id: 'emea-nl-ams-azr' + } +] + +@sys.description('Optional. Is the test actively being monitored.') +param enabled bool = true + +@sys.description('Optional. Interval in seconds between test runs for this WebTest.') +param frequency int = 300 + +@sys.description('Optional. Seconds until this WebTest will timeout and fail.') +param timeout int = 30 + +@sys.description('Optional. Allow for retries should this WebTest fail.') +param retryEnabled bool = true + +@sys.description('Optional. The collection of validation rule properties.') +param validationRules object = {} + +@sys.description('Optional. An XML configuration specification for a WebTest.') +param configuration object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@sys.description('Optional. Specify the type of lock.') +param lock string = '' + +@sys.description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource webtest 'Microsoft.Insights/webtests@2022-06-15' = { + name: name + location: location + tags: tags + properties: { + Kind: kind + Locations: locations + Name: webTestName + Description: description + SyntheticMonitorId: syntheticMonitorId + Enabled: enabled + Frequency: frequency + Timeout: timeout + RetryEnabled: retryEnabled + Request: request + ValidationRules: validationRules + Configuration: configuration + } +} + +resource webtest_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${webtest.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: webtest +} + +module webtest_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-WebTests-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: webtest.id + } +}] + +@sys.description('The name of the webtest.') +output name string = webtest.name + +@sys.description('The resource ID of the webtest.') +output resourceId string = webtest.id + +@sys.description('The resource group the resource was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The location the resource was deployed into.') +output location string = webtest.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/readme.md new file mode 100644 index 000000000..dbc36cebd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/readme.md @@ -0,0 +1,324 @@ +# Web Tests `[Microsoft.Insights/webTests]` + +This module deploys Web Tests. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/webtests` | [2022-06-15](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2022-06-15/webtests) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the webtest. | +| `request` | object | The collection of request properties. | +| `tags` | object | A single hidden-link tag pointing to an existing AI component is required. | +| `webTestName` | string | User defined name if this WebTest. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `configuration` | object | `{object}` | | An XML configuration specification for a WebTest. | +| `description` | string | `''` | | User defined description for this WebTest. | +| `enabled` | bool | `True` | | Is the test actively being monitored. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `frequency` | int | `300` | | Interval in seconds between test runs for this WebTest. | +| `kind` | string | `'standard'` | `[multistep, ping, standard]` | The kind of WebTest that this web test watches. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `locations` | array | `[System.Management.Automation.OrderedHashtable, System.Management.Automation.OrderedHashtable, System.Management.Automation.OrderedHashtable, System.Management.Automation.OrderedHashtable, System.Management.Automation.OrderedHashtable]` | | List of where to physically run the tests from to give global coverage for accessibility of your application. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `retryEnabled` | bool | `True` | | Allow for retries should this WebTest fail. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `syntheticMonitorId` | string | `[parameters('name')]` | | Unique ID of this WebTest. | +| `timeout` | int | `30` | | Seconds until this WebTest will timeout and fail. | +| `validationRules` | object | `{object}` | | The collection of validation rule properties. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the webtest. | +| `resourceGroupName` | string | The resource group the resource was deployed into. | +| `resourceId` | string | The resource ID of the webtest. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module webTests './Microsoft.Insights/webTests/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iwtcom' + params: { + // Required parameters + name: '<>iwtcom001' + request: { + HttpVerb: 'GET' + RequestUrl: 'https://learn.microsoft.com/en-us/' + } + tags: { + 'hidden-link:${nestedDependencies.outputs.appInsightResourceId}': 'Resource' + } + webTestName: 'wt<>$iwtcom001' + // Non-required parameters + enableDefaultTelemetry: '' + locations: [ + { + Id: 'emea-nl-ams-azr' + } + ] + lock: 'CanNotDelete' + syntheticMonitorId: '<>iwtcom001' + } +} +``` + +
+

+ +

+ +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": "<>iwtcom001" + }, + "request": { + "value": { + "HttpVerb": "GET", + "RequestUrl": "https://learn.microsoft.com/en-us/" + } + }, + "tags": { + "value": { + "hidden-link:${nestedDependencies.outputs.appInsightResourceId}": "Resource" + } + }, + "webTestName": { + "value": "wt<>$iwtcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "locations": { + "value": [ + { + "Id": "emea-nl-ams-azr" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "syntheticMonitorId": { + "value": "<>iwtcom001" + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module webTests './Microsoft.Insights/webTests/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-iwtmin' + params: { + // Required parameters + name: '<>iwtmin001' + request: { + HttpVerb: 'GET' + RequestUrl: 'https://learn.microsoft.com/en-us/' + } + tags: { + 'hidden-link:${nestedDependencies.outputs.appInsightResourceId}': 'Resource' + } + webTestName: 'wt<>$iwtmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>iwtmin001" + }, + "request": { + "value": { + "HttpVerb": "GET", + "RequestUrl": "https://learn.microsoft.com/en-us/" + } + }, + "tags": { + "value": { + "hidden-link:${nestedDependencies.outputs.appInsightResourceId}": "Resource" + } + }, + "webTestName": { + "value": "wt<>$iwtmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Insights/webTests/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..19a13565d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,70 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Resource Policy Contributor': subscriptionResourceId('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') +} + +resource userMsi 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(userMsi.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: userMsi +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/deploy.test.bicep new file mode 100644 index 000000000..2c2d448c1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/.test/common/deploy.test.bicep @@ -0,0 +1,64 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.managedidentity.userassignedidentities-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'miuaicom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep new file mode 100644 index 000000000..188347458 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep @@ -0,0 +1,80 @@ +@description('Optional. Name of the User Assigned Identity.') +param name string = guid(resourceGroup().id) + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource userMsi 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: name + location: location + tags: tags +} + +resource userMsi_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${userMsi.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: userMsi +} + +module userMsi_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-UserMSI-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: userMsi.id + } +}] + +@description('The name of the user assigned identity.') +output name string = userMsi.name + +@description('The resource ID of the user assigned identity.') +output resourceId string = userMsi.id + +@description('The principal ID of the user assigned identity.') +output principalId string = userMsi.properties.principalId + +@description('The resource ID of the user assigned identity') +output clientId string = userMsi.properties.clientId + +@description('The resource group the user assigned identity was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = userMsi.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md new file mode 100644 index 000000000..71f9188ee --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/readme.md @@ -0,0 +1,229 @@ +# User Assigned Identities `[Microsoft.ManagedIdentity/userAssignedIdentities]` + +This module deploys a user assigned identity. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.ManagedIdentity/userAssignedIdentities` | [2018-11-30](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2018-11-30/userAssignedIdentities) | + +## Parameters + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `name` | string | `[guid(resourceGroup().id)]` | | Name of the User Assigned Identity. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the user assigned identity. | +| `principalId` | string | The principal ID of the user assigned identity. | +| `resourceGroupName` | string | The resource group the user assigned identity was deployed into. | +| `resourceId` | string | The resource ID of the user assigned identity. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module userAssignedIdentities './Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-miuaicom' + params: { + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + name: '<>miuaicom001' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": { + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "name": { + "value": "<>miuaicom001" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/.test/common/deploy.test.bicep new file mode 100644 index 000000000..421eaac1b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/.test/common/deploy.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.applicationGatewayWebApplicationFirewallPolicies-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nagwafpcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + policySettings: { + fileUploadLimitInMb: 10 + state: 'Enabled' + mode: 'Prevention' + } + managedRules: { + managedRuleSets: [ + { + ruleSetType: 'OWASP' + ruleSetVersion: '3.2' + ruleGroupOverrides: [] + } + { + ruleSetType: 'Microsoft_BotManagerRuleSet' + ruleSetVersion: '0.1' + ruleGroupOverrides: [] + } + ] + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/deploy.bicep new file mode 100644 index 000000000..606b417d2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/deploy.bicep @@ -0,0 +1,55 @@ +@description('Required. Name of the Application Gateway WAF policy.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Describes the managedRules structure.') +param managedRules object = {} + +@description('Optional. The custom rules inside the policy.') +param customRules array = [] + +@description('Optional. The PolicySettings for policy.') +param policySettings object = {} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource applicationGatewayWAFPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2022-01-01' = { + name: name + location: location + tags: tags + properties: { + managedRules: managedRules + customRules: customRules + policySettings: policySettings + } +} + +@description('The name of the application gateway WAF policy.') +output name string = applicationGatewayWAFPolicy.name + +@description('The resource ID of the application gateway WAF policy.') +output resourceId string = applicationGatewayWAFPolicy.id + +@description('The resource group the application gateway WAF policy was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = applicationGatewayWAFPolicy.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/readme.md new file mode 100644 index 000000000..7780fa64b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/readme.md @@ -0,0 +1,195 @@ +# Application Gateway WebApp Firewall Policies `[Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies]` + +This module deploys a WAF policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-01-01/ApplicationGatewayWebApplicationFirewallPolicies) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Gateway WAF policy. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `customRules` | array | `[]` | The custom rules inside the policy. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `managedRules` | object | `{object}` | Describes the managedRules structure. | +| `policySettings` | object | `{object}` | The PolicySettings for policy. | +| `tags` | object | `{object}` | Resource tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application gateway WAF policy. | +| `resourceGroupName` | string | The resource group the application gateway WAF policy was deployed into. | +| `resourceId` | string | The resource ID of the application gateway WAF policy. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module applicationGatewayWebApplicationFirewallPolicies './Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nagwafpcom' + params: { + // Required parameters + name: '<>nagwafpcom001' + // Non-required parameters + enableDefaultTelemetry: '' + managedRules: { + managedRuleSets: [ + { + ruleGroupOverrides: [] + ruleSetType: 'OWASP' + ruleSetVersion: '3.2' + } + { + ruleGroupOverrides: [] + ruleSetType: 'Microsoft_BotManagerRuleSet' + ruleSetVersion: '0.1' + } + ] + } + policySettings: { + fileUploadLimitInMb: 10 + mode: 'Prevention' + state: 'Enabled' + } + tags: { + Environment: 'Non-Prod' + 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": "<>nagwafpcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "managedRules": { + "value": { + "managedRuleSets": [ + { + "ruleGroupOverrides": [], + "ruleSetType": "OWASP", + "ruleSetVersion": "3.2" + }, + { + "ruleGroupOverrides": [], + "ruleSetType": "Microsoft_BotManagerRuleSet", + "ruleSetVersion": "0.1" + } + ] + } + }, + "policySettings": { + "value": { + "fileUploadLimitInMb": 10, + "mode": "Prevention", + "state": "Enabled" + } + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..2af219017 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource applicationGateway 'Microsoft.Network/applicationGateways@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(applicationGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: applicationGateway +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/dependencies.bicep new file mode 100644 index 000000000..46d1b47f0 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/dependencies.bicep @@ -0,0 +1,117 @@ +@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@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2022-01-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } +} + +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('../../../../.shared/.scripts/Set-CertificateInKeyVault.ps1') + } +} + +@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 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 diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/deploy.test.bicep new file mode 100644 index 000000000..88b290019 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/.test/common/deploy.test.bicep @@ -0,0 +1,442 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.applicationgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nagcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + publicIPName: 'dep-<>-pip-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + certDeploymentScriptName: 'dep-<>-ds-${serviceShort}' + keyVaultName: 'dep-<>-kv-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +var appGWName = '<>${serviceShort}001' +var appGWExpectedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/applicationGateways/${appGWName}' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: appGWName + 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 + } + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + enableHttp2: true + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: nestedDependencies.outputs.subnetResourceId + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: nestedDependencies.outputs.publicIPResourceId + } + } + } + ] + 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.subnetResourceId + } + } + } + ] + httpListeners: [ + { + name: 'public443' + properties: { + frontendIPConfiguration: { + id: '${appGWExpectedResourceID}/frontendIPConfigurations/public' + } + frontendPort: { + id: '${appGWExpectedResourceID}/frontendPorts/port443' + } + hostNames: [] + protocol: 'https' + requireServerNameIndication: false + sslCertificate: { + id: '${appGWExpectedResourceID}/sslCertificates/<>-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/<>-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: 'CanNotDelete' + 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: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '<>-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: nestedDependencies.outputs.certificateSecretUrl + } + } + ] + userAssignedIdentities: { + '${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: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/deploy.bicep new file mode 100644 index 000000000..b295ae2f5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/deploy.bicep @@ -0,0 +1,398 @@ +@description('Required. Name of the Application Gateway.') +@maxLength(24) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@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. 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_Medium' + +@description('Optional. The number of Application instances to be configured.') +@minValue(1) +@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. Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely.') +@minValue(0) +@maxValue(365) +param diagnosticLogsRetentionInDays int = 365 + +@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.') +param diagnosticStorageAccountId string = '' + +@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.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'ApplicationGatewayAccessLog' + 'ApplicationGatewayPerformanceLog' + 'ApplicationGatewayFirewallLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +var identityType = !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@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 telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource applicationGateway 'Microsoft.Network/applicationGateways@2022-07-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: { + enableRequestBuffering: enableRequestBuffering + enableResponseBuffering: enableResponseBuffering + } + 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)) { + name: '${applicationGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: applicationGateway +} + +resource applicationGateway_diagnosticSettingName 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId + workspaceId: empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId + eventHubAuthorizationRuleId: empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId + eventHubName: empty(diagnosticEventHubName) ? null : diagnosticEventHubName + metrics: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsMetrics + logs: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsLogs + } + scope: applicationGateway +} + +module applicationGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: applicationGateway.id + } +}] + +@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 diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/readme.md new file mode 100644 index 000000000..ec29520fb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/readme.md @@ -0,0 +1,1073 @@ +# Network Application Gateways `[Microsoft.Network/applicationGateways]` + +This module deploys Network ApplicationGateways. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/applicationGateways) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Gateway. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `authenticationCertificates` | array | `[]` | | Authentication certificates of the application gateway resource. | +| `autoscaleMaxCapacity` | int | `-1` | | Upper bound on number of Application Gateway capacity. | +| `autoscaleMinCapacity` | int | `-1` | | Lower bound on number of Application Gateway capacity. | +| `backendAddressPools` | array | `[]` | | Backend address pool of the application gateway resource. | +| `backendHttpSettingsCollection` | array | `[]` | | Backend http settings of the application gateway resource. | +| `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` | int | `2` | | The number of Application instances to be configured. | +| `customErrorConfigurations` | array | `[]` | | Custom error configurations of the application gateway resource. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, ApplicationGatewayAccessLog, ApplicationGatewayFirewallLog, ApplicationGatewayPerformanceLog]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | 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. | +| `diagnosticWorkspaceId` | 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. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableFips` | bool | `False` | | Whether FIPS is enabled on the application gateway resource. | +| `enableHttp2` | bool | `False` | | Whether HTTP2 is enabled on the application gateway resource. | +| `enableRequestBuffering` | bool | `False` | | Enable request buffering. | +| `enableResponseBuffering` | bool | `False` | | Enable response buffering. | +| `firewallPolicyId` | string | `''` | | The resource ID of an associated firewall policy. Should be configured for security reasons. | +| `frontendIPConfigurations` | array | `[]` | | Frontend IP addresses of the application gateway resource. | +| `frontendPorts` | array | `[]` | | Frontend ports of the application gateway resource. | +| `gatewayIPConfigurations` | array | `[]` | | Subnets of the application gateway resource. | +| `httpListeners` | array | `[]` | | Http listeners of the application gateway resource. | +| `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` | array | `[]` | | Load distribution policies of the application gateway resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `privateLinkConfigurations` | array | `[]` | | PrivateLink configurations on application gateway. | +| `probes` | array | `[]` | | Probes of the application gateway resource. | +| `redirectConfigurations` | array | `[]` | | Redirect configurations of the application gateway resource. | +| `requestRoutingRules` | array | `[]` | | Request routing rules of the application gateway resource. | +| `rewriteRuleSets` | array | `[]` | | Rewrite rules for the application gateway resource. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `routingRules` | array | `[]` | | Routing rules of the application gateway resource. | +| `sku` | string | `'WAF_Medium'` | `[Standard_Large, Standard_Medium, Standard_Small, Standard_v2, WAF_Large, WAF_Medium, WAF_v2]` | The name of the SKU for the Application Gateway. | +| `sslCertificates` | array | `[]` | | SSL certificates of the application gateway resource. | +| `sslPolicyCipherSuites` | array | `[TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]` | `[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]` | Ssl cipher suites to be enabled in the specified order to application gateway. | +| `sslPolicyMinProtocolVersion` | string | `'TLSv1_2'` | `[TLSv1_0, TLSv1_1, TLSv1_2, TLSv1_3]` | Ssl protocol enums. | +| `sslPolicyName` | string | `''` | `['', AppGwSslPolicy20150501, AppGwSslPolicy20170401, AppGwSslPolicy20170401S, AppGwSslPolicy20220101, AppGwSslPolicy20220101S]` | Ssl predefined policy name enums. | +| `sslPolicyType` | string | `'Custom'` | `[Custom, CustomV2, Predefined]` | Type of Ssl Policy. | +| `sslProfiles` | array | `[]` | | SSL profiles of the application gateway resource. | +| `tags` | object | `{object}` | | Resource tags. | +| `trustedClientCertificates` | array | `[]` | | Trusted client certificates of the application gateway resource. | +| `trustedRootCertificates` | array | `[]` | | Trusted Root certificates of the application gateway resource. | +| `urlPathMaps` | array | `[]` | | URL path map of the application gateway resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `webApplicationFirewallConfiguration` | object | `{object}` | | Application gateway web application firewall configuration. Should be configured for security reasons. | +| `zones` | array | `[]` | | A list of availability zones denoting where the resource needs to come from. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | 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 + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module applicationGateways './Microsoft.Network/applicationGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nagcom' + 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 + } + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + enableHttp2: true + frontendIPConfigurations: [ + { + name: 'private' + properties: { + privateIPAddress: '10.0.0.20' + privateIPAllocationMethod: 'Static' + subnet: { + id: '' + } + } + } + { + name: 'public' + properties: { + privateIPAllocationMethod: 'Dynamic' + 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 + } + } + ] + lock: 'CanNotDelete' + 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: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + sku: 'WAF_v2' + sslCertificates: [ + { + name: '<>-az-apgw-x-001-ssl-certificate' + properties: { + keyVaultSecretId: '' + } + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + userAssignedIdentities: { + '': {} + } + 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' + } + } +} +``` + +
+

+ +

+ +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 + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enableHttp2": { + "value": true + }, + "frontendIPConfigurations": { + "value": [ + { + "name": "private", + "properties": { + "privateIPAddress": "10.0.0.20", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "" + } + } + }, + { + "name": "public", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "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 + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "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": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sku": { + "value": "WAF_v2" + }, + "sslCertificates": { + "value": [ + { + "name": "<>-az-apgw-x-001-ssl-certificate", + "properties": { + "keyVaultSecretId": "" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "userAssignedIdentities": { + "value": { + "": {} + } + }, + "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" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..5d36c1929 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(applicationSecurityGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: applicationSecurityGroup +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/deploy.test.bicep new file mode 100644 index 000000000..28e4f3a79 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/.test/common/deploy.test.bicep @@ -0,0 +1,64 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.applicationsecuritygroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nasgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/deploy.bicep new file mode 100644 index 000000000..d0fbdd92c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/deploy.bicep @@ -0,0 +1,75 @@ +@description('Required. Name of the Application Security Group.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2022-07-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource applicationSecurityGroup_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${applicationSecurityGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: applicationSecurityGroup +} + +module applicationSecurityGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppSecurityGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: applicationSecurityGroup.id + } +}] + +@description('The resource group the application security group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the application security group.') +output resourceId string = applicationSecurityGroup.id + +@description('The name of the application security group.') +output name string = applicationSecurityGroup.name + +@description('The location the resource was deployed into.') +output location string = applicationSecurityGroup.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/readme.md new file mode 100644 index 000000000..92d5d69bb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/readme.md @@ -0,0 +1,237 @@ +# Application Security Groups `[Microsoft.Network/applicationSecurityGroups]` + +This module deploys an application security group. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/applicationSecurityGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/applicationSecurityGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Application Security Group. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the application security group. | +| `resourceGroupName` | string | The resource group the application security group was deployed into. | +| `resourceId` | string | The resource ID of the application security group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module applicationSecurityGroups './Microsoft.Network/applicationSecurityGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nasgcom' + params: { + // Required parameters + name: '<>nasgcom001' + // Non-required parameters + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nasgcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/applicationSecurityGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..2f3b24534 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource azureFirewall 'Microsoft.Network/azureFirewalls@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(azureFirewall.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: azureFirewall +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/dependencies.bicep new file mode 100644 index 000000000..8d8fc5001 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/dependencies.bicep @@ -0,0 +1,53 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureFirewallSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2022-01-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/deploy.test.bicep new file mode 100644 index 000000000..5ef9fddad --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/addpip/deploy.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nafaddpip' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + publicIPName: 'dep-<>-pip-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: nestedDependencies.outputs.publicIPResourceId + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/dependencies.bicep new file mode 100644 index 000000000..d8b427942 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/dependencies.bicep @@ -0,0 +1,64 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureFirewallSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2022-01-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 +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/deploy.test.bicep new file mode 100644 index 000000000..1ecfde8d8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/common/deploy.test.bicep @@ -0,0 +1,173 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nafcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + publicIPName: 'dep-<>-pip-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + applicationRuleCollections: [ + { + name: 'allow-app-rules' + properties: { + action: { + type: 'allow' + } + priority: 100 + rules: [ + { + fqdnTags: [ + 'AppServiceEnvironment' + 'WindowsUpdate' + ] + name: 'allow-ase-tags' + protocols: [ + { + port: '80' + protocolType: 'HTTP' + } + { + port: '443' + protocolType: 'HTTPS' + } + ] + sourceAddresses: [ + '*' + ] + } + { + name: 'allow-ase-management' + protocols: [ + { + port: '80' + protocolType: 'HTTP' + } + { + port: '443' + protocolType: 'HTTPS' + } + ] + sourceAddresses: [ + '*' + ] + targetFqdns: [ + 'bing.com' + ] + } + ] + } + } + ] + publicIPResourceID: nestedDependencies.outputs.publicIPResourceId + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + networkRuleCollections: [ + { + name: 'allow-network-rules' + properties: { + action: { + type: 'allow' + } + priority: 100 + rules: [ + { + destinationAddresses: [ + '*' + ] + destinationPorts: [ + '12000' + '123' + ] + name: 'allow-ntp' + protocols: [ + 'Any' + ] + sourceAddresses: [ + '*' + ] + } + ] + } + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + zones: [ + '1' + '2' + '3' + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/dependencies.bicep new file mode 100644 index 000000000..1748a2e50 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/dependencies.bicep @@ -0,0 +1,41 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureFirewallSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/deploy.test.bicep new file mode 100644 index 000000000..4048a5293 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/custompip/deploy.test.bicep @@ -0,0 +1,80 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nafcstpip' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + publicIPAddressObject: { + diagnosticLogCategoriesToEnable: [ + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + 'DDoSProtectionNotifications' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + name: 'new-<>-pip-${serviceShort}' + publicIPAllocationMethod: 'Static' + publicIPPrefixResourceId: '' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + skuName: 'Standard' + skuTier: 'Regional' + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/dependencies.bicep new file mode 100644 index 000000000..4fe1ddd8c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/dependencies.bicep @@ -0,0 +1,46 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual WAN to create.') +param virtualWanName string + +@description('Required. The name of the Virtual Hub to create.') +param virtualHubName string + +@description('Required. The name of the Firewall Policy to create.') +param firewallPolicyName string + +resource virtualWan 'Microsoft.Network/virtualWans@2021-08-01' = { + name: virtualWanName + location: location + properties: { + disableVpnEncryption: false + allowBranchToBranchTraffic: true + type: 'Standard' + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2021-08-01' = { + name: virtualHubName + location: location + properties: { + addressPrefix: '10.1.0.0/16' + virtualWan: { + id: virtualWan.id + } + } +} + +resource policy 'Microsoft.Network/firewallPolicies@2021-08-01' = { + name: firewallPolicyName + location: location + properties: { + threatIntelMode: 'Alert' + } +} + +@description('The resource ID of the created Virtual Hub.') +output virtualHubResourceId string = virtualHub.id + +@description('The resource ID of the created Firewall Policy.') +output firewallPolicyResourceId string = policy.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/deploy.test.bicep new file mode 100644 index 000000000..f87103da7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubcommon/deploy.test.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nafhubcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualWanName: 'dep-<>-vwan-${serviceShort}' + virtualHubName: 'dep-<>-vhub-${serviceShort}' + firewallPolicyName: 'dep-<>-afwp-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + firewallPolicyId: nestedDependencies.outputs.firewallPolicyResourceId + virtualHubId: nestedDependencies.outputs.virtualHubResourceId + hubIPAddresses: { + publicIPs: { + count: 1 + } + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/dependencies.bicep new file mode 100644 index 000000000..21324f286 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/dependencies.bicep @@ -0,0 +1,32 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual WAN to create.') +param virtualWanName string + +@description('Required. The name of the Virtual Hub to create.') +param virtualHubName string + +resource virtualWan 'Microsoft.Network/virtualWans@2021-08-01' = { + name: virtualWanName + location: location + properties: { + disableVpnEncryption: false + allowBranchToBranchTraffic: true + type: 'Standard' + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2021-08-01' = { + name: virtualHubName + location: location + properties: { + addressPrefix: '10.1.0.0/16' + virtualWan: { + id: virtualWan.id + } + } +} + +@description('The resource ID of the created Virtual Hub.') +output virtualHubResourceId string = virtualHub.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/deploy.test.bicep new file mode 100644 index 000000000..828939eb5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/hubmin/deploy.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nafhubmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualWanName: 'dep-<>-vwan-${serviceShort}' + virtualHubName: 'dep-<>-vhub-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + virtualHubId: nestedDependencies.outputs.virtualHubResourceId + hubIPAddresses: { + publicIPs: { + count: 1 + } + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/dependencies.bicep new file mode 100644 index 000000000..bd6b937f6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/dependencies.bicep @@ -0,0 +1,29 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureFirewallSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/deploy.test.bicep new file mode 100644 index 000000000..57b363b9c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/.test/min/deploy.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.azurefirewalls-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nafmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/deploy.bicep new file mode 100644 index 000000000..3b9d22b13 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/deploy.bicep @@ -0,0 +1,332 @@ +@description('Required. Name of the Azure Firewall.') +param name string + +@description('Optional. Tier of an Azure Firewall.') +@allowed([ + 'Standard' + 'Premium' +]) +param azureSkuTier string = 'Standard' + +@description('Conditional. Shared services Virtual Network resource ID. The virtual network ID containing AzureFirewallSubnet. If a Public IP is not provided, then the Public IP that is created as part of this module will be applied with the subnet provided in this variable. Required if `virtualHubId` is empty.') +param vNetId string = '' + +@description('Optional. The Public IP resource ID to associate to the AzureFirewallSubnet. If empty, then the Public IP that is created as part of this module will be applied to the AzureFirewallSubnet.') +param publicIPResourceID string = '' + +@description('Optional. This is to add any additional Public IP configurations on top of the Public IP with subnet IP configuration.') +param additionalPublicIpConfigurations array = [] + +@description('Optional. Specifies if a Public IP should be created by default if one is not provided.') +param isCreateDefaultPublicIP bool = true + +@description('Optional. Specifies the properties of the Public IP to create and be used by Azure Firewall. If it\'s not provided and publicIPAddressId is empty, a \'-pip\' suffix will be appended to the Firewall\'s name.') +param publicIPAddressObject object = {} + +@description('Optional. Collection of application rule collections used by Azure Firewall.') +param applicationRuleCollections array = [] + +@description('Optional. Collection of network rule collections used by Azure Firewall.') +param networkRuleCollections array = [] + +@description('Optional. Collection of NAT rule collections used by Azure Firewall.') +param natRuleCollections array = [] + +@description('Optional. Resource ID of the Firewall Policy that should be attached.') +param firewallPolicyId string = '' + +@description('Conditional. IP addresses associated with AzureFirewall. Required if `virtualHubId` is supplied.') +param hubIPAddresses object = {} + +@description('Conditional. The virtualHub resource ID to which the firewall belongs. Required if `vNetId` is empty.') +param virtualHubId string = '' + +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +@description('Optional. The operation mode for Threat Intel.') +param threatIntelMode string = 'Deny' + +@description('Optional. Zone numbers e.g. 1,2,3.') +param zones array = [ + '1' + '2' + '3' +] + +@description('Optional. Diagnostic Storage Account resource identifier.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Log Analytics workspace resource identifier.') +param diagnosticWorkspaceId string = '' + +@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 diagnosticLogsRetentionInDays int = 365 + +@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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the Azure Firewall resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'AzureFirewallApplicationRule' + 'AzureFirewallNetworkRule' + 'AzureFirewallDnsProxy' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var additionalPublicIpConfigurationsVar = [for ipConfiguration in additionalPublicIpConfigurations: { + name: ipConfiguration.name + properties: { + publicIPAddress: contains(ipConfiguration, 'publicIPAddressResourceId') ? { + id: ipConfiguration.publicIPAddressResourceId + } : null + } +}] + +// ---------------------------------------------------------------------------- +// Prep ipConfigurations object AzureFirewallSubnet for different uses cases: +// 1. Use existing Public IP +// 2. Use new Public IP created in this module +// 3. Do not use a Public IP if isCreateDefaultPublicIP is false + +var subnetVar = { + subnet: { + id: '${vNetId}/subnets/AzureFirewallSubnet' // The subnet name must be AzureFirewallSubnet + } +} +var existingPip = { + publicIPAddress: { + id: publicIPResourceID + } +} +var newPip = { + publicIPAddress: (empty(publicIPResourceID) && isCreateDefaultPublicIP) ? { + id: publicIPAddress.outputs.resourceId + } : null +} + +var azureSkuName = empty(vNetId) ? 'AZFW_Hub' : 'AZFW_VNet' + +var ipConfigurations = concat([ + { + name: !empty(publicIPResourceID) ? last(split(publicIPResourceID, '/')) : publicIPAddress.outputs.name + //Use existing Public IP, new Public IP created in this module, or none if isCreateDefaultPublicIP is false + properties: union(subnetVar, !empty(publicIPResourceID) ? existingPip : {}, (isCreateDefaultPublicIP ? newPip : {})) + } + ], additionalPublicIpConfigurationsVar) + +// ---------------------------------------------------------------------------- + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// create a Public IP address if one is not provided and the flag is true +module publicIPAddress '../../Microsoft.Network/publicIPAddresses/deploy.bicep' = if (empty(publicIPResourceID) && isCreateDefaultPublicIP && azureSkuName == 'AZFW_VNet') { + name: '${uniqueString(deployment().name, location)}-Firewall-PIP' + params: { + name: contains(publicIPAddressObject, 'name') ? (!(empty(publicIPAddressObject.name)) ? publicIPAddressObject.name : '${name}-pip') : '${name}-pip' + publicIPPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId') ? (!(empty(publicIPAddressObject.publicIPPrefixResourceId)) ? publicIPAddressObject.publicIPPrefixResourceId : '') : '' + publicIPAllocationMethod: contains(publicIPAddressObject, 'publicIPAllocationMethod') ? (!(empty(publicIPAddressObject.publicIPAllocationMethod)) ? publicIPAddressObject.publicIPAllocationMethod : 'Static') : 'Static' + skuName: contains(publicIPAddressObject, 'skuName') ? (!(empty(publicIPAddressObject.skuName)) ? publicIPAddressObject.skuName : 'Standard') : 'Standard' + skuTier: contains(publicIPAddressObject, 'skuTier') ? (!(empty(publicIPAddressObject.skuTier)) ? publicIPAddressObject.skuTier : 'Regional') : 'Regional' + roleAssignments: contains(publicIPAddressObject, 'roleAssignments') ? (!empty(publicIPAddressObject.roleAssignments) ? publicIPAddressObject.roleAssignments : []) : [] + diagnosticMetricsToEnable: contains(publicIPAddressObject, 'diagnosticMetricsToEnable') ? (!(empty(publicIPAddressObject.diagnosticMetricsToEnable)) ? publicIPAddressObject.diagnosticMetricsToEnable : [ + 'AllMetrics' + ]) : [ + 'AllMetrics' + ] + diagnosticLogCategoriesToEnable: contains(publicIPAddressObject, 'diagnosticLogCategoriesToEnable') ? publicIPAddressObject.diagnosticLogCategoriesToEnable : [ + 'allLogs' + ] + location: location + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + lock: lock + tags: tags + zones: zones + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource azureFirewall 'Microsoft.Network/azureFirewalls@2022-07-01' = { + name: name + location: location + zones: length(zones) == 0 ? null : zones + tags: tags + properties: azureSkuName == 'AZFW_VNet' ? { + threatIntelMode: threatIntelMode + firewallPolicy: !empty(firewallPolicyId) ? { + id: firewallPolicyId + } : null + ipConfigurations: ipConfigurations + 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 + } + dependsOn: [ + publicIPAddress + ] +} + +resource azureFirewall_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${azureFirewall.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: azureFirewall +} + +resource azureFirewall_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: azureFirewall +} + +module azureFirewall_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AzFW-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: azureFirewall.id + } +}] + +@description('The resource ID of the Azure Firewall.') +output resourceId string = azureFirewall.id + +@description('The name of the Azure Firewall.') +output name string = azureFirewall.name + +@description('The resource group the Azure firewall was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The private IP of the Azure firewall.') +output privateIp string = contains(azureFirewall.properties, 'ipConfigurations') ? azureFirewall.properties.ipConfigurations[0].properties.privateIPAddress : '' + +@description('The Public IP configuration object for the Azure Firewall Subnet.') +output ipConfAzureFirewallSubnet object = contains(azureFirewall.properties, 'ipConfigurations') ? azureFirewall.properties.ipConfigurations[0] : {} + +@description('List of Application Rule Collections.') +output applicationRuleCollections array = applicationRuleCollections + +@description('List of Network Rule Collections.') +output networkRuleCollections array = networkRuleCollections + +@description('Collection of NAT rule collections used by Azure Firewall.') +output natRuleCollections array = natRuleCollections + +@description('The location the resource was deployed into.') +output location string = azureFirewall.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/readme.md new file mode 100644 index 000000000..2de3fe8f4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/readme.md @@ -0,0 +1,974 @@ +# Azure Firewalls `[Microsoft.Network/azureFirewalls]` + +This module deploys a firewall. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/azureFirewalls` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/azureFirewalls) | +| `Microsoft.Network/publicIPAddresses` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/publicIPAddresses) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Firewall. | + +**Conditional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `hubIPAddresses` | object | `{object}` | IP addresses associated with AzureFirewall. Required if `virtualHubId` is supplied. | +| `virtualHubId` | string | `''` | The virtualHub resource ID to which the firewall belongs. Required if `vNetId` is empty. | +| `vNetId` | string | `''` | Shared services Virtual Network resource ID. The virtual network ID containing AzureFirewallSubnet. If a Public IP is not provided, then the Public IP that is created as part of this module will be applied with the subnet provided in this variable. Required if `virtualHubId` is empty. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `additionalPublicIpConfigurations` | array | `[]` | | This is to add any additional Public IP configurations on top of the Public IP with subnet IP configuration. | +| `applicationRuleCollections` | array | `[]` | | Collection of application rule collections used by Azure Firewall. | +| `azureSkuTier` | string | `'Standard'` | `[Premium, Standard]` | Tier of an Azure Firewall. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, AzureFirewallApplicationRule, AzureFirewallDnsProxy, AzureFirewallNetworkRule]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Diagnostic Storage Account resource identifier. | +| `diagnosticWorkspaceId` | string | `''` | | Log Analytics workspace resource identifier. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `firewallPolicyId` | string | `''` | | Resource ID of the Firewall Policy that should be attached. | +| `isCreateDefaultPublicIP` | bool | `True` | | Specifies if a Public IP should be created by default if one is not provided. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natRuleCollections` | array | `[]` | | Collection of NAT rule collections used by Azure Firewall. | +| `networkRuleCollections` | array | `[]` | | Collection of network rule collections used by Azure Firewall. | +| `publicIPAddressObject` | object | `{object}` | | Specifies the properties of the Public IP to create and be used by Azure Firewall. If it's not provided and publicIPAddressId is empty, a '-pip' suffix will be appended to the Firewall's name. | +| `publicIPResourceID` | string | `''` | | The Public IP resource ID to associate to the AzureFirewallSubnet. If empty, then the Public IP that is created as part of this module will be applied to the AzureFirewallSubnet. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the Azure Firewall resource. | +| `threatIntelMode` | string | `'Deny'` | `[Alert, Deny, Off]` | The operation mode for Threat Intel. | +| `zones` | array | `[1, 2, 3]` | | Zone numbers e.g. 1,2,3. | + + +### Parameter Usage: `additionalPublicIpConfigurations` + +Create additional public ip configurations from existing public ips + +

+ +Parameter JSON format + +```json +"additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01" + }, + { + "name": "ipConfig02", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01' + } + { + name: 'ipConfig02' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02' + } +] +``` + +
+ + +### Parameter Usage: `publicIPAddressObject` + +The Public IP Address object to create as part of the module. This will be created if `isCreateDefaultPublicIP` is true (which it is by default). If not provided, the name and other configurations will be set by default. + + +
+ +Parameter JSON format + +```json +"publicIPAddressObject": { + "value": { + "name": "adp-<>-az-pip-custom-x-fw", + "publicIPPrefixResourceId": "", + "publicIPAllocationMethod": "Static", + "skuName": "Standard", + "skuTier": "Regional", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "" + ] + } + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "diagnosticLogCategoriesToEnable": [ + "DDoSProtectionNotifications", + "DDoSMitigationFlowLogs", + "DDoSMitigationReports" + ] + } +} +``` + +
+ + + +
+ +Bicep format + + +```bicep +publicIPAddressObject: { + name: 'mypip' + publicIPPrefixResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPPrefixes/myprefix' + publicIPAllocationMethod: 'Dynamic' + skuName: 'Basic' + skuTier: 'Regional' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '' + ] + } + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + diagnosticLogCategoriesToEnable: [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ] +} +``` + +
+ + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +
+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `applicationRuleCollections` | array | List of Application Rule Collections. | +| `ipConfAzureFirewallSubnet` | object | The Public IP configuration object for the Azure Firewall Subnet. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the Azure Firewall. | +| `natRuleCollections` | array | Collection of NAT rule collections used by Azure Firewall. | +| `networkRuleCollections` | array | List of Network Rule Collections. | +| `privateIp` | string | The private IP of the Azure firewall. | +| `resourceGroupName` | string | The resource group the Azure firewall was deployed into. | +| `resourceId` | string | The resource ID of the Azure Firewall. | + +## Considerations + +The `applicationRuleCollections` parameter accepts a JSON Array of AzureFirewallApplicationRule objects. +The `networkRuleCollections` parameter accepts a JSON Array of AzureFirewallNetworkRuleCollection objects. + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Network/publicIPAddresses` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Addpip

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nafaddpip' + params: { + // Required parameters + name: '<>nafaddpip001' + // Non-required parameters + additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: '' + } + ] + enableDefaultTelemetry: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vNetId: '' + } +} +``` + +
+

+ +

+ +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": "<>nafaddpip001" + }, + // Non-required parameters + "additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "vNetId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 2: Common

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nafcom' + params: { + // Required parameters + name: '<>nafcom001' + // Non-required parameters + applicationRuleCollections: [ + { + name: 'allow-app-rules' + properties: { + action: { + type: 'allow' + } + priority: 100 + rules: [ + { + fqdnTags: [ + 'AppServiceEnvironment' + 'WindowsUpdate' + ] + name: 'allow-ase-tags' + protocols: [ + { + port: '80' + protocolType: 'HTTP' + } + { + port: '443' + protocolType: 'HTTPS' + } + ] + sourceAddresses: [ + '*' + ] + } + { + name: 'allow-ase-management' + protocols: [ + { + port: '80' + protocolType: 'HTTP' + } + { + port: '443' + protocolType: 'HTTPS' + } + ] + sourceAddresses: [ + '*' + ] + targetFqdns: [ + 'bing.com' + ] + } + ] + } + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + networkRuleCollections: [ + { + name: 'allow-network-rules' + properties: { + action: { + type: 'allow' + } + priority: 100 + rules: [ + { + destinationAddresses: [ + '*' + ] + destinationPorts: [ + '12000' + '123' + ] + name: 'allow-ntp' + protocols: [ + 'Any' + ] + sourceAddresses: [ + '*' + ] + } + ] + } + } + ] + publicIPResourceID: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vNetId: '' + 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": "<>nafcom001" + }, + // Non-required parameters + "applicationRuleCollections": { + "value": [ + { + "name": "allow-app-rules", + "properties": { + "action": { + "type": "allow" + }, + "priority": 100, + "rules": [ + { + "fqdnTags": [ + "AppServiceEnvironment", + "WindowsUpdate" + ], + "name": "allow-ase-tags", + "protocols": [ + { + "port": "80", + "protocolType": "HTTP" + }, + { + "port": "443", + "protocolType": "HTTPS" + } + ], + "sourceAddresses": [ + "*" + ] + }, + { + "name": "allow-ase-management", + "protocols": [ + { + "port": "80", + "protocolType": "HTTP" + }, + { + "port": "443", + "protocolType": "HTTPS" + } + ], + "sourceAddresses": [ + "*" + ], + "targetFqdns": [ + "bing.com" + ] + } + ] + } + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkRuleCollections": { + "value": [ + { + "name": "allow-network-rules", + "properties": { + "action": { + "type": "allow" + }, + "priority": 100, + "rules": [ + { + "destinationAddresses": [ + "*" + ], + "destinationPorts": [ + "12000", + "123" + ], + "name": "allow-ntp", + "protocols": [ + "Any" + ], + "sourceAddresses": [ + "*" + ] + } + ] + } + } + ] + }, + "publicIPResourceID": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "vNetId": { + "value": "" + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

+ +

Example 3: Custompip

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nafcstpip' + params: { + // Required parameters + name: '<>nafcstpip001' + // Non-required parameters + enableDefaultTelemetry: '' + publicIPAddressObject: { + diagnosticLogCategoriesToEnable: [ + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + 'DDoSProtectionNotifications' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + name: 'new-<>-pip-nafcstpip' + publicIPAllocationMethod: 'Static' + publicIPPrefixResourceId: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + skuTier: 'Regional' + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vNetId: '' + } +} +``` + +
+

+ +

+ +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": "<>nafcstpip001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "publicIPAddressObject": { + "value": { + "diagnosticLogCategoriesToEnable": [ + "DDoSMitigationFlowLogs", + "DDoSMitigationReports", + "DDoSProtectionNotifications" + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "name": "new-<>-pip-nafcstpip", + "publicIPAllocationMethod": "Static", + "publicIPPrefixResourceId": "", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "skuName": "Standard", + "skuTier": "Regional" + } + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "vNetId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 4: Hubcommon

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nafhubcom' + params: { + // Required parameters + name: '<>nafhubcom001' + // Non-required parameters + enableDefaultTelemetry: '' + firewallPolicyId: '' + hubIPAddresses: { + publicIPs: { + count: 1 + } + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + virtualHubId: '' + } +} +``` + +
+

+ +

+ +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": "<>nafhubcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "firewallPolicyId": { + "value": "" + }, + "hubIPAddresses": { + "value": { + "publicIPs": { + "count": 1 + } + } + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "virtualHubId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 5: Hubmin

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nafhubmin' + params: { + // Required parameters + name: '<>nafhubmin001' + // Non-required parameters + enableDefaultTelemetry: '' + hubIPAddresses: { + publicIPs: { + count: 1 + } + } + virtualHubId: '' + } +} +``` + +
+

+ +

+ +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": "<>nafhubmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "hubIPAddresses": { + "value": { + "publicIPs": { + "count": 1 + } + } + }, + "virtualHubId": { + "value": "" + } + } +} +``` + +
+

+ +

Example 6: Min

+ +
+ +via Bicep module + +```bicep +module azureFirewalls './Microsoft.Network/azureFirewalls/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nafmin' + params: { + // Required parameters + name: '<>nafmin001' + // Non-required parameters + enableDefaultTelemetry: '' + vNetId: '' + } +} +``` + +
+

+ +

+ +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": "<>nafmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "vNetId": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/azureFirewalls/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..1ba022cf4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource azureBastion 'Microsoft.Network/bastionHosts@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(azureBastion.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: azureBastion +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/dependencies.bicep new file mode 100644 index 000000000..6cb3743d3 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/dependencies.bicep @@ -0,0 +1,59 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureBastionSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2022-01-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/deploy.test.bicep new file mode 100644 index 000000000..f24eb334c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/common/deploy.test.bicep @@ -0,0 +1,93 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.bastionhosts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nbhcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + publicIPName: 'dep-<>-pip-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + bastionSubnetPublicIpResourceId: nestedDependencies.outputs.publicIPResourceId + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + scaleUnits: 4 + skuName: 'Standard' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/dependencies.bicep new file mode 100644 index 000000000..7f9f2ef02 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/dependencies.bicep @@ -0,0 +1,41 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureBastionSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/deploy.test.bicep new file mode 100644 index 000000000..bbd66ae5e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/custompip/deploy.test.bicep @@ -0,0 +1,80 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.bastionhosts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nbhctmpip' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + publicIPAddressObject: { + diagnosticLogCategoriesToEnable: [ + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + 'DDoSProtectionNotifications' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + name: '<>${serviceShort}001-pip' + allocationMethod: 'Static' + publicIPPrefixResourceId: '' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + skuName: 'Standard' + skuTier: 'Regional' + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/dependencies.bicep new file mode 100644 index 000000000..a2cb671f6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/dependencies.bicep @@ -0,0 +1,30 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'AzureBastionSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/deploy.test.bicep new file mode 100644 index 000000000..39327b1c1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/.test/min/deploy.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.bastionhosts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nbhmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vNetId: nestedDependencies.outputs.virtualNetworkResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/deploy.bicep new file mode 100644 index 000000000..fb9700b45 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/deploy.bicep @@ -0,0 +1,255 @@ +@description('Required. Name of the Azure Bastion resource.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Shared services Virtual Network resource identifier.') +param vNetId string + +@description('Optional. The Public IP resource ID to associate to the azureBastionSubnet. If empty, then the Public IP that is created as part of this module will be applied to the azureBastionSubnet.') +param bastionSubnetPublicIpResourceId string = '' + +@description('Optional. Specifies if a Public IP should be created by default if one is not provided.') +param isCreateDefaultPublicIP bool = true + +@description('Optional. Specifies the properties of the Public IP to create and be used by Azure Bastion. If it\'s not provided and publicIPAddressResourceId is empty, a \'-pip\' suffix will be appended to the Bastion\'s name.') +param publicIPAddressObject object = {} + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@allowed([ + 'Basic' + 'Standard' +]) +@description('Optional. The SKU of this Bastion Host.') +param skuName string = 'Basic' + +@description('Optional. Choose to disable or enable Copy Paste.') +param disableCopyPaste bool = false + +@description('Optional. Choose to disable or enable File Copy.') +param enableFileCopy bool = true + +@description('Optional. Choose to disable or enable IP Connect.') +param enableIpConnect bool = false + +@description('Optional. Choose to disable or enable Shareable Link.') +param enableShareableLink bool = false + +@description('Optional. The scale units for the Bastion Host resource.') +param scaleUnits int = 2 + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'BastionAuditLogs' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var enableTunneling = skuName == 'Standard' ? true : null + +var scaleUnitsVar = skuName == 'Basic' ? 2 : scaleUnits + +// ---------------------------------------------------------------------------- +// Prep ipConfigurations object AzureBastionSubnet for different uses cases: +// 1. Use existing Public IP +// 2. Use new Public IP created in this module +// 3. Do not use a Public IP if isCreateDefaultPublicIP is false +var subnetVar = { + subnet: { + id: '${vNetId}/subnets/AzureBastionSubnet' // The subnet name must be AzureBastionSubnet + } +} +var existingPip = { + publicIPAddress: { + id: bastionSubnetPublicIpResourceId + } +} +var newPip = { + publicIPAddress: (empty(bastionSubnetPublicIpResourceId) && isCreateDefaultPublicIP) ? { + id: publicIPAddress.outputs.resourceId + } : null +} + +var ipConfigurations = [ + { + name: 'IpConfAzureBastionSubnet' + //Use existing Public IP, new Public IP created in this module, or none if isCreateDefaultPublicIP is false + properties: union(subnetVar, !empty(bastionSubnetPublicIpResourceId) ? existingPip : {}, (isCreateDefaultPublicIP ? newPip : {})) + } +] + +var enableReferencedModulesTelemetry = false + +// ---------------------------------------------------------------------------- + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module publicIPAddress '../publicIPAddresses/deploy.bicep' = if (empty(bastionSubnetPublicIpResourceId) && isCreateDefaultPublicIP) { + name: '${uniqueString(deployment().name, location)}-Bastion-PIP' + params: { + name: contains(publicIPAddressObject, 'name') ? publicIPAddressObject.name : '${name}-pip' + diagnosticLogCategoriesToEnable: contains(publicIPAddressObject, 'diagnosticLogCategoriesToEnable') ? publicIPAddressObject.diagnosticLogCategoriesToEnable : [ + 'allLogs' + ] + diagnosticMetricsToEnable: contains(publicIPAddressObject, 'diagnosticMetricsToEnable') ? publicIPAddressObject.diagnosticMetricsToEnable : [ + 'AllMetrics' + ] + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: location + lock: lock + publicIPAddressVersion: contains(publicIPAddressObject, 'publicIPAddressVersion') ? publicIPAddressObject.publicIPAddressVersion : 'IPv4' + publicIPAllocationMethod: contains(publicIPAddressObject, 'publicIPAllocationMethod') ? publicIPAddressObject.publicIPAllocationMethod : 'Static' + publicIPPrefixResourceId: contains(publicIPAddressObject, 'publicIPPrefixResourceId') ? publicIPAddressObject.publicIPPrefixResourceId : '' + roleAssignments: contains(publicIPAddressObject, 'roleAssignments') ? publicIPAddressObject.roleAssignments : [] + skuName: contains(publicIPAddressObject, 'skuName') ? publicIPAddressObject.skuName : 'Standard' + skuTier: contains(publicIPAddressObject, 'skuTier') ? publicIPAddressObject.skuTier : 'Regional' + tags: tags + zones: contains(publicIPAddressObject, 'zones') ? publicIPAddressObject.zones : [] + } +} + +var bastionpropertiesVar = skuName == 'Standard' ? { + scaleUnits: scaleUnitsVar + ipConfigurations: ipConfigurations + enableTunneling: enableTunneling + disableCopyPaste: disableCopyPaste + enableFileCopy: enableFileCopy + enableIpConnect: enableIpConnect + enableShareableLink: enableShareableLink +} : { + scaleUnits: scaleUnitsVar + ipConfigurations: ipConfigurations +} + +resource azureBastion 'Microsoft.Network/bastionHosts@2022-01-01' = { + name: name + location: location + tags: tags + sku: { + name: skuName + } + properties: bastionpropertiesVar +} + +resource azureBastion_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${azureBastion.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: azureBastion +} + +resource azureBastion_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: azureBastion +} + +module azureBastion_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-Bastion-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: azureBastion.id + } +}] + +@description('The resource group the Azure Bastion was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name the Azure Bastion.') +output name string = azureBastion.name + +@description('The resource ID the Azure Bastion.') +output resourceId string = azureBastion.id + +@description('The location the resource was deployed into.') +output location string = azureBastion.location + +@description('The Public IPconfiguration object for the AzureBastionSubnet.') +output ipConfAzureBastionSubnet object = azureBastion.properties.ipConfigurations[0] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/readme.md new file mode 100644 index 000000000..92c8db6e7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/readme.md @@ -0,0 +1,592 @@ +# Bastion Hosts `[Microsoft.Network/bastionHosts]` + +This module deploys a bastion host. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/bastionHosts` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-01-01/bastionHosts) | +| `Microsoft.Network/publicIPAddresses` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/publicIPAddresses) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Bastion resource. | +| `vNetId` | string | Shared services Virtual Network resource identifier. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `bastionSubnetPublicIpResourceId` | string | `''` | | The Public IP resource ID to associate to the azureBastionSubnet. If empty, then the Public IP that is created as part of this module will be applied to the azureBastionSubnet. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, BastionAuditLogs]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableCopyPaste` | bool | `False` | | Choose to disable or enable Copy Paste. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableFileCopy` | bool | `True` | | Choose to disable or enable File Copy. | +| `enableIpConnect` | bool | `False` | | Choose to disable or enable IP Connect. | +| `enableShareableLink` | bool | `False` | | Choose to disable or enable Shareable Link. | +| `isCreateDefaultPublicIP` | bool | `True` | | Specifies if a Public IP should be created by default if one is not provided. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicIPAddressObject` | object | `{object}` | | Specifies the properties of the Public IP to create and be used by Azure Bastion. If it's not provided and publicIPAddressResourceId is empty, a '-pip' suffix will be appended to the Bastion's name. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `scaleUnits` | int | `2` | | The scale units for the Bastion Host resource. | +| `skuName` | string | `'Basic'` | `[Basic, Standard]` | The SKU of this Bastion Host. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `additionalPublicIpConfigurations` + +Create additional public ip configurations from existing public ips + +

+ +Parameter JSON format + +```json +"additionalPublicIpConfigurations": { + "value": [ + { + "name": "ipConfig01", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01" + }, + { + "name": "ipConfig02", + "publicIPAddressResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +additionalPublicIpConfigurations: [ + { + name: 'ipConfig01' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-01' + } + { + name: 'ipConfig02' + publicIPAddressResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPAddresses/adp-<>-az-pip-x-fw-02' + } +] +``` + +
+ + +### Parameter Usage: `publicIPAddressObject` + +The Public IP Address object to create as part of the module. This will be created if `isCreateDefaultPublicIP` is true (which it is by default). If not provided, the name and other configurations will be set by default. + + +
+ +Parameter JSON format + +```json +"publicIPAddressObject": { + "value": { + "name": "adp-<>-az-pip-custom-x-fw", + "publicIPPrefixResourceId": "", + "publicIPAllocationMethod": "Static", + "skuName": "Standard", + "skuTier": "Regional", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "" + ] + } + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "diagnosticLogCategoriesToEnable": [ + "DDoSProtectionNotifications", + "DDoSMitigationFlowLogs", + "DDoSMitigationReports" + ] + } +} +``` + +
+ + + +
+ +Bicep format + + +```bicep +publicIPAddressObject: { + name: 'mypip' + publicIPPrefixResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/publicIPPrefixes/myprefix' + publicIPAllocationMethod: 'Dynamic' + skuName: 'Basic' + skuTier: 'Regional' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '' + ] + } + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + diagnosticLogCategoriesToEnable: [ + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + ] +} +``` + +
+ + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +
+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `ipConfAzureBastionSubnet` | object | The Public IPconfiguration object for the AzureBastionSubnet. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name the Azure Bastion. | +| `resourceGroupName` | string | The resource group the Azure Bastion was deployed into. | +| `resourceId` | string | The resource ID the Azure Bastion. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Network/publicIPAddresses` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module bastionHosts './Microsoft.Network/bastionHosts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nbhcom' + params: { + // Required parameters + name: '<>nbhcom001' + vNetId: '' + // Non-required parameters + bastionSubnetPublicIpResourceId: '' + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + disableCopyPaste: true + enableDefaultTelemetry: '' + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + scaleUnits: 4 + skuName: 'Standard' + tags: { + Environment: 'Non-Prod' + 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": "<>nbhcom001" + }, + "vNetId": { + "value": "" + }, + // Non-required parameters + "bastionSubnetPublicIpResourceId": { + "value": "" + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "disableCopyPaste": { + "value": true + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enableFileCopy": { + "value": false + }, + "enableIpConnect": { + "value": false + }, + "enableShareableLink": { + "value": false + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scaleUnits": { + "value": 4 + }, + "skuName": { + "value": "Standard" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Custompip

+ +
+ +via Bicep module + +```bicep +module bastionHosts './Microsoft.Network/bastionHosts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nbhctmpip' + params: { + // Required parameters + name: '<>nbhctmpip001' + vNetId: '' + // Non-required parameters + enableDefaultTelemetry: '' + publicIPAddressObject: { + allocationMethod: 'Static' + diagnosticLogCategoriesToEnable: [ + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' + 'DDoSProtectionNotifications' + ] + diagnosticMetricsToEnable: [ + 'AllMetrics' + ] + name: '<>nbhctmpip001-pip' + publicIPPrefixResourceId: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + skuTier: 'Regional' + } + tags: { + Environment: 'Non-Prod' + 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": "<>nbhctmpip001" + }, + "vNetId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "publicIPAddressObject": { + "value": { + "allocationMethod": "Static", + "diagnosticLogCategoriesToEnable": [ + "DDoSMitigationFlowLogs", + "DDoSMitigationReports", + "DDoSProtectionNotifications" + ], + "diagnosticMetricsToEnable": [ + "AllMetrics" + ], + "name": "<>nbhctmpip001-pip", + "publicIPPrefixResourceId": "", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "skuName": "Standard", + "skuTier": "Regional" + } + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module bastionHosts './Microsoft.Network/bastionHosts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nbhmin' + params: { + // Required parameters + name: '<>nbhmin001' + vNetId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nbhmin001" + }, + "vNetId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/bastionHosts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/dependencies.bicep new file mode 100644 index 000000000..fde57e708 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/dependencies.bicep @@ -0,0 +1,132 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the primary Public IP to create.') +param primaryPublicIPName string + +@description('Required. The name of the primary VNET to create.') +param primaryVirtualNetworkName string + +@description('Required. The name of the primary Virtual Network Gateway to create.') +param primaryVirtualNetworkGatewayName string + +@description('Required. The name of the secondary Public IP to create.') +param secondaryPublicIPName string + +@description('Required. The name of the secondary VNET to create.') +param secondaryVirtualNetworkName string + +@description('Required. The name of the secondary Virtual Network Gateway to create.') +param secondaryVirtualNetworkGatewayName string + +resource primaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: primaryVirtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.0.0/24' + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: '10.0.0.0/24' + } + } + ] + } +} + +resource primaryPublicIP 'Microsoft.Network/publicIPAddresses@2022-01-01' = { + name: primaryPublicIPName + location: location +} + +resource primaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2021-08-01' = { + name: primaryVirtualNetworkGatewayName + location: location + properties: { + gatewayType: 'Vpn' + ipConfigurations: [ + { + name: 'default' + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: primaryVirtualNetwork.properties.subnets[0].id + } + publicIPAddress: { + id: primaryPublicIP.id + } + } + } + ] + vpnType: 'RouteBased' + vpnGatewayGeneration: 'Generation2' + sku: { + name: 'VpnGw2' + tier: 'VpnGw2' + } + } +} + +resource secondaryVirtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: secondaryVirtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.1.0/24' + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: '10.0.1.0/24' + } + } + ] + } +} + +resource secondaryPublicIP 'Microsoft.Network/publicIPAddresses@2022-01-01' = { + name: secondaryPublicIPName + location: location +} + +resource secondaryVNETGateway 'Microsoft.Network/virtualNetworkGateways@2021-08-01' = { + name: secondaryVirtualNetworkGatewayName + location: location + properties: { + gatewayType: 'Vpn' + ipConfigurations: [ + { + name: 'default' + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: secondaryVirtualNetwork.properties.subnets[0].id + } + publicIPAddress: { + id: secondaryPublicIP.id + } + } + } + ] + vpnType: 'RouteBased' + vpnGatewayGeneration: 'Generation2' + sku: { + name: 'VpnGw2' + tier: 'VpnGw2' + } + } +} + +@description('The resource ID of the created primary Virtual Network Gateway.') +output primaryVNETGatewayResourceID string = primaryVNETGateway.id + +@description('The resource ID of the created secondary Virtual Network Gateway.') +output secondaryVNETGatewayResourceID string = secondaryVNETGateway.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/deploy.test.bicep new file mode 100644 index 000000000..4714dffd6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/.test/vnet2vnet/deploy.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.connections-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ncvtv' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + primaryPublicIPName: 'dep-<>-pip-${serviceShort}-1' + primaryVirtualNetworkName: 'dep-<>-vnet-${serviceShort}-1' + primaryVirtualNetworkGatewayName: 'dep-<>-vpn-gw-${serviceShort}-1' + secondaryPublicIPName: 'dep-<>-pip-${serviceShort}-2' + secondaryVirtualNetworkName: 'dep-<>-vnet-${serviceShort}-2' + secondaryVirtualNetworkGatewayName: 'dep-<>-vpn-gw-${serviceShort}-2' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + virtualNetworkGateway1: { + id: nestedDependencies.outputs.primaryVNETGatewayResourceID + } + enableBgp: false + lock: 'CanNotDelete' + virtualNetworkGateway2: { + id: nestedDependencies.outputs.secondaryVNETGatewayResourceID + } + connectionType: 'Vnet2Vnet' + vpnSharedKey: password + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/deploy.bicep new file mode 100644 index 000000000..ea100cb80 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/deploy.bicep @@ -0,0 +1,163 @@ +@description('Required. Remote connection name.') +param name string + +@description('Optional. Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways.') +param vpnSharedKey string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Gateway connection connectionType.') +@allowed([ + 'IPsec' + 'Vnet2Vnet' + 'ExpressRoute' + 'VPNClient' +]) +param connectionType string = 'IPsec' + +@description('Optional. Value to specify if BGP is enabled or not.') +param enableBgp bool = false + +@allowed([ + 'Default' + 'InitiatorOnly' + 'ResponderOnly' +]) +@description('Optional. The connection connectionMode for this connection. Available for IPSec connections.') +param connectionMode string = 'Default' + +@allowed([ + 'IKEv1' + 'IKEv2' +]) +@description('Optional. Connection connectionProtocol used for this connection. Available for IPSec connections.') +param connectionProtocol string = 'IKEv2' + +@minValue(9) +@maxValue(3600) +@description('Optional. The dead peer detection timeout of this connection in seconds. Setting the timeout to shorter periods will cause IKE to rekey more aggressively, causing the connection to appear to be disconnected in some instances. The general recommendation is to set the timeout between 30 to 45 seconds.') +param dpdTimeoutSeconds int = 45 + +@description('Optional. Enable policy-based traffic selectors.') +param usePolicyBasedTrafficSelectors bool = false + +@description('Optional. Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route.') +param enablePrivateLinkFastPath bool = false + +@description('Optional. Bypass ExpressRoute Gateway for data forwarding. Only available when connection connectionType is Express Route.') +param expressRouteGatewayBypass bool = false + +@description('Optional. Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property.') +param useLocalAzureIpAddress bool = false + +@description('Optional. The IPSec Policies to be considered by this connection.') +param customIPSecPolicy object = { + saLifeTimeSeconds: 0 + saDataSizeKilobytes: 0 + ipsecEncryption: '' + ipsecIntegrity: '' + ikeEncryption: '' + ikeIntegrity: '' + dhGroup: '' + pfsGroup: '' +} + +@description('Optional. The weight added to routes learned from this BGP speaker.') +param routingWeight int = -1 + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the connectionType of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. The primary Virtual Network Gateway.') +param virtualNetworkGateway1 object + +@description('Optional. The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet].') +param virtualNetworkGateway2 object = {} + +@description('Optional. The remote peer. Used for connection connectionType [ExpressRoute].') +param peer object = {} + +@description('Optional. The local network gateway. Used for connection connectionType [IPsec].') +param localNetworkGateway2 object = {} + +var customIPSecPolicyVar = [ + { + saLifeTimeSeconds: customIPSecPolicy.saLifeTimeSeconds + saDataSizeKilobytes: customIPSecPolicy.saDataSizeKilobytes + ipsecEncryption: customIPSecPolicy.ipsecEncryption + ipsecIntegrity: customIPSecPolicy.ipsecIntegrity + ikeEncryption: customIPSecPolicy.ikeEncryption + ikeIntegrity: customIPSecPolicy.ikeIntegrity + dhGroup: customIPSecPolicy.dhGroup + pfsGroup: customIPSecPolicy.pfsGroup + } +] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource connection 'Microsoft.Network/connections@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + connectionType: connectionType + connectionMode: connectionType == 'IPsec' ? connectionMode : null + connectionProtocol: connectionType == 'IPsec' ? connectionProtocol : null + dpdTimeoutSeconds: connectionType == 'IPsec' ? dpdTimeoutSeconds : null + enablePrivateLinkFastPath: connectionType == 'ExpressRoute' ? enablePrivateLinkFastPath : null + expressRouteGatewayBypass: connectionType == 'ExpressRoute' ? expressRouteGatewayBypass : null + virtualNetworkGateway1: virtualNetworkGateway1 + virtualNetworkGateway2: connectionType == 'Vnet2Vnet' ? virtualNetworkGateway2 : null + localNetworkGateway2: connectionType == 'IPsec' ? localNetworkGateway2 : null + peer: connectionType == 'ExpressRoute' ? peer : null + sharedKey: connectionType != 'ExpressRoute' ? vpnSharedKey : null + usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors + ipsecPolicies: !empty(customIPSecPolicy.ipsecEncryption) ? customIPSecPolicyVar : customIPSecPolicy.ipsecEncryption + routingWeight: routingWeight != -1 ? routingWeight : null + enableBgp: enableBgp + useLocalAzureIpAddress: connectionType == 'IPsec' ? useLocalAzureIpAddress : null + } +} + +resource connection_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${connection.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: connection +} + +@description('The resource group the remote connection was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the remote connection.') +output name string = connection.name + +@description('The resource ID of the remote connection.') +output resourceId string = connection.id + +@description('The location the resource was deployed into.') +output location string = connection.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/readme.md new file mode 100644 index 000000000..f4f5e4d29 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/readme.md @@ -0,0 +1,410 @@ +# Virtual Network Gateway Connections `[Microsoft.Network/connections]` + +This template deploys a virtual network gateway connection. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Network/connections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/connections) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Remote connection name. | +| `virtualNetworkGateway1` | object | The primary Virtual Network Gateway. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `connectionMode` | string | `'Default'` | `[Default, InitiatorOnly, ResponderOnly]` | The connection connectionMode for this connection. Available for IPSec connections. | +| `connectionProtocol` | string | `'IKEv2'` | `[IKEv1, IKEv2]` | Connection connectionProtocol used for this connection. Available for IPSec connections. | +| `connectionType` | string | `'IPsec'` | `[ExpressRoute, IPsec, Vnet2Vnet, VPNClient]` | Gateway connection connectionType. | +| `customIPSecPolicy` | object | `{object}` | | The IPSec Policies to be considered by this connection. | +| `dpdTimeoutSeconds` | int | `45` | | The dead peer detection timeout of this connection in seconds. Setting the timeout to shorter periods will cause IKE to rekey more aggressively, causing the connection to appear to be disconnected in some instances. The general recommendation is to set the timeout between 30 to 45 seconds. | +| `enableBgp` | bool | `False` | | Value to specify if BGP is enabled or not. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enablePrivateLinkFastPath` | bool | `False` | | Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled. Only available when connection connectionType is Express Route. | +| `expressRouteGatewayBypass` | bool | `False` | | Bypass ExpressRoute Gateway for data forwarding. Only available when connection connectionType is Express Route. | +| `localNetworkGateway2` | object | `{object}` | | The local network gateway. Used for connection connectionType [IPsec]. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the connectionType of lock. | +| `peer` | object | `{object}` | | The remote peer. Used for connection connectionType [ExpressRoute]. | +| `routingWeight` | int | `-1` | | The weight added to routes learned from this BGP speaker. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `useLocalAzureIpAddress` | bool | `False` | | Use private local Azure IP for the connection. Only available for IPSec Virtual Network Gateways that use the Azure Private IP Property. | +| `usePolicyBasedTrafficSelectors` | bool | `False` | | Enable policy-based traffic selectors. | +| `virtualNetworkGateway2` | object | `{object}` | | The remote Virtual Network Gateway. Used for connection connectionType [Vnet2Vnet]. | +| `vpnSharedKey` | string | `''` | | Specifies a VPN shared key. The same value has to be specified on both Virtual Network Gateways. | + + +### Parameter Usage: `virtualNetworkGateway1` + +The primary virtual network gateway object. + +

+ +Parameter JSON format + +```json +"virtualNetworkGateway1": { + "value": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway01" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +virtualNetworkGateway1: { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway01' +} +``` + +
+

+ +### Parameter Usage: `virtualNetworkGateway2` + +The secondary virtual network gateway used for VNET to VNET connections. + +

+ +Parameter JSON format + +```json +"virtualNetworkGateway2" : { + "value": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway02" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +virtualNetworkGateway2 : { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworkGateways/myGateway02' +} +``` + +
+

+ +### Parameter Usage: `localNetworkGateway2` + +The local virtual network gateway object. + +

+ +Parameter JSON format + +```json +"localNetworkGateway2": { + "value": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/localNetworkGateways/myGateway" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +localNetworkGateway2: { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/localNetworkGateways/myGateway' +} +``` + +
+

+ +### Parameter Usage: `peer` + +The remote peer object used for ExpressRoute connections + +

+ +Parameter JSON format + +```json +"peer": { + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/expressRouteCircuits/expressRoute" +} +``` + +
+ +
+ +Bicep format + +```bicep +'peer': { + id: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRG/providers/Microsoft.Network/expressRouteCircuits/expressRoute' +} +``` + +
+

+ +### Parameter Usage: `customIPSecPolicy` + +If ipsecEncryption parameter is empty, customIPSecPolicy will not be deployed. The parameter file should look like below. + +

+ +Parameter JSON format + +```json +"customIPSecPolicy": { + "value": { + "saLifeTimeSeconds": 0, + "saDataSizeKilobytes": 0, + "ipsecEncryption": "", + "ipsecIntegrity": "", + "ikeEncryption": "", + "ikeIntegrity": "", + "dhGroup": "", + "pfsGroup": "" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +customIPSecPolicy: { + saLifeTimeSeconds: 0 + saDataSizeKilobytes: 0 + ipsecEncryption: '' + ipsecIntegrity: '' + ikeEncryption: '' + ikeIntegrity: '' + dhGroup: '' + pfsGroup: '' +} +``` + +
+

+ +Format of the full customIPSecPolicy parameter in parameter file. + +

+ +Parameter JSON format + +```json +"customIPSecPolicy": { + "value": { + "saLifeTimeSeconds": 28800, + "saDataSizeKilobytes": 102400000, + "ipsecEncryption": "AES256", + "ipsecIntegrity": "SHA256", + "ikeEncryption": "AES256", + "ikeIntegrity": "SHA256", + "dhGroup": "DHGroup14", + "pfsGroup": "None" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +customIPSecPolicy: { + saLifeTimeSeconds: 28800 + saDataSizeKilobytes: 102400000 + ipsecEncryption: 'AES256' + ipsecIntegrity: 'SHA256' + ikeEncryption: 'AES256' + ikeIntegrity: 'SHA256' + dhGroup: 'DHGroup14' + pfsGroup: 'None' +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the remote connection. | +| `resourceGroupName` | string | The resource group the remote connection was deployed into. | +| `resourceId` | string | The resource ID of the remote connection. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Vnet2vnet

+ +
+ +via Bicep module + +```bicep +module connections './Microsoft.Network/connections/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ncvtv' + params: { + // Required parameters + name: '<>ncvtv001' + virtualNetworkGateway1: { + id: '' + } + // Non-required parameters + connectionType: 'Vnet2Vnet' + enableBgp: false + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + virtualNetworkGateway2: { + id: '' + } + vpnSharedKey: '' + } +} +``` + +
+

+ +

+ +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": "<>ncvtv001" + }, + "virtualNetworkGateway1": { + "value": { + "id": "" + } + }, + // Non-required parameters + "connectionType": { + "value": "Vnet2Vnet" + }, + "enableBgp": { + "value": false + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "virtualNetworkGateway2": { + "value": { + "id": "" + } + }, + "vpnSharedKey": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/connections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..ae50048d7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(ddosProtectionPlan.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: ddosProtectionPlan +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/deploy.test.bicep new file mode 100644 index 000000000..28f0abe76 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/common/deploy.test.bicep @@ -0,0 +1,64 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.ddosprotectionplans-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ndppcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/min/deploy.test.bicep new file mode 100644 index 000000000..751e03027 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.ddosprotectionplans-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ndppmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/deploy.bicep new file mode 100644 index 000000000..134b4c478 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/deploy.bicep @@ -0,0 +1,76 @@ +@description('Required. Name of the DDoS protection plan to assign the VNET to.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource ddosProtectionPlan_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${ddosProtectionPlan.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: ddosProtectionPlan +} + +module ddosProtectionPlan_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-DDoSProtectionPlan-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: ddosProtectionPlan.id + } +}] + +@description('The resource group the DDOS protection plan was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the DDOS protection plan.') +output resourceId string = ddosProtectionPlan.id + +@description('The name of the DDOS protection plan.') +output name string = ddosProtectionPlan.name + +@description('The location the resource was deployed into.') +output location string = ddosProtectionPlan.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/readme.md new file mode 100644 index 000000000..d3f7ff9d8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/readme.md @@ -0,0 +1,282 @@ +# DDoS Protection Plans `[Microsoft.Network/ddosProtectionPlans]` + +This template deploys a DDoS protection plan. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/ddosProtectionPlans` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/ddosProtectionPlans) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the DDoS protection plan to assign the VNET to. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the DDOS protection plan. | +| `resourceGroupName` | string | The resource group the DDOS protection plan was deployed into. | +| `resourceId` | string | The resource ID of the DDOS protection plan. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module ddosProtectionPlans './Microsoft.Network/ddosProtectionPlans/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ndppcom' + params: { + // Required parameters + name: '<>ndppcom001' + // Non-required parameters + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>ndppcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module ddosProtectionPlans './Microsoft.Network/ddosProtectionPlans/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ndppmin' + params: { + // Required parameters + name: '<>ndppmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>ndppmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ddosProtectionPlans/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..491a75d38 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource dnsResolver 'Microsoft.Network/ddosProtectionPlans@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(dnsResolver.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: dnsResolver +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/dependencies.bicep new file mode 100644 index 000000000..8d3ff6c57 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/dependencies.bicep @@ -0,0 +1,56 @@ +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.10.100.0/24' + ] + } + subnets: [ + { + name: 'pdnsin' + properties: { + addressPrefix: '10.10.100.0/25' + delegations: [ + { + name: 'dnsdel' + properties: { + serviceName: 'Microsoft.Network/dnsResolvers' + } + } + ] + } + } + { + name: 'pdnsout' + properties: { + addressPrefix: '10.10.100.128/25' + delegations: [ + { + name: 'dnsdel' + properties: { + serviceName: 'Microsoft.Network/dnsResolvers' + } + } + ] + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkId string = virtualNetwork.id + +@description('The resource ID of the created inbound endpoint Virtual Network Subnet.') +output subnetResourceId_dnsIn string = virtualNetwork.properties.subnets[0].id + +@description('The resource ID of the created outbound endpoint Virtual Network Subnet.') +output subnetResourceId_dnsOut string = virtualNetwork.properties.subnets[1].id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/deploy.test.bicep new file mode 100644 index 000000000..5a07ebd47 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/.test/common/deploy.test.bicep @@ -0,0 +1,68 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.dnsResolvers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ndrcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + virtualNetworkId: nestedDependencies.outputs.virtualNetworkId + inboundEndpoints: [ + { + name: '<>-az-pdnsin-x-001' + subnetId: nestedDependencies.outputs.subnetResourceId_dnsIn + } + ] + outboundEndpoints: [ + { + name: '<>-az-pdnsout-x-001' + subnetId: nestedDependencies.outputs.subnetResourceId_dnsOut + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/deploy.bicep new file mode 100644 index 000000000..e1c3b2e98 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/deploy.bicep @@ -0,0 +1,117 @@ +@description('Required. Name of the Private DNS Resolver.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Required. ResourceId of the virtual network to attach the Private DNS Resolver to.') +param virtualNetworkId string + +@description('Optional. Outbound Endpoints for Private DNS Resolver.') +param outboundEndpoints array = [] + +@description('Optional. Inbound Endpoints for Private DNS Resolver.') +param inboundEndpoints array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource dnsResolver 'Microsoft.Network/dnsResolvers@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + virtualNetwork: { + id: virtualNetworkId + } + } +} + +resource dnsResolver_inboundEndpoint 'Microsoft.Network/dnsResolvers/inboundEndpoints@2022-07-01' = [for inboundEndpoint in inboundEndpoints: { + name: inboundEndpoint.name + parent: dnsResolver + location: location + tags: tags + properties: { + ipConfigurations: [ + { + subnet: { + id: inboundEndpoint.subnetId + } + } + ] + } +}] + +resource dnsResolver_outboundEndpoint 'Microsoft.Network/dnsResolvers/outboundEndpoints@2022-07-01' = [for outboundEndpoint in outboundEndpoints: { + name: outboundEndpoint.name + parent: dnsResolver + location: location + tags: tags + properties: { + subnet: { + id: outboundEndpoint.subnetId + } + } +}] + +resource dnsResolver_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${dnsResolver.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: dnsResolver +} + +module dnsResolver_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-dnsResolver-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: dnsResolver.id + } +}] + +@description('The resource group the Private DNS Resolver was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the Private DNS Resolver.') +output resourceId string = dnsResolver.id + +@description('The name of the Private DNS Resolver.') +output name string = dnsResolver.name + +@description('The location the resource was deployed into.') +output location string = dnsResolver.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/readme.md new file mode 100644 index 000000000..d6b2a22ac --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/readme.md @@ -0,0 +1,332 @@ +# Network DnsResolvers `[Microsoft.Network/dnsResolvers]` + +This module deploys Network DnsResolvers. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/dnsResolvers` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/dnsResolvers) | +| `Microsoft.Network/dnsResolvers/inboundEndpoints` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/dnsResolvers/inboundEndpoints) | +| `Microsoft.Network/dnsResolvers/outboundEndpoints` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/dnsResolvers/outboundEndpoints) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Private DNS Resolver. | +| `virtualNetworkId` | string | ResourceId of the virtual network to attach the Private DNS Resolver to. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `inboundEndpoints` | array | `[]` | | Inbound Endpoints for Private DNS Resolver. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `outboundEndpoints` | array | `[]` | | Outbound Endpoints for Private DNS Resolver. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `inboundEndpoints` + +Create a inbound endpoint for Azure DNS Private Resolver + +

+ +Parameter JSON format + +```json + "inboundEndpoints": { + "value": [ + { + "name": "<>-az-pdnsin-x-001", + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002/subnets/<>-az-subnet-x-001" + } + ] + }, +``` + +
+ +
+ +Bicep format + +```bicep +inboundEndpoints: [ + { + name: '<>-az-pdnsin-x-001' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002/subnets/<>-az-subnet-x-001' + } + { + name: '<>-az-pdnsin-x-002' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002/subnets/<>-az-subnet-x-002' + } +] +``` + +
+

+ +### Parameter Usage: `outboundEndpoints` + +Create a inbound endpoint for Azure DNS Private Resolver + +

+ +Parameter JSON format + +```json + "outboundEndpoints": { + "value": [ + { + "name": "<>-az-pdnsout-x-001", + "subnetId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002/subnets/<>-az-subnet-x-001" + } + ] + }, +``` + +
+ +
+ +Bicep format + +```bicep +outboundEndpoints: [ + { + name: '<>-az-pdnsout-x-001' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002/subnets/<>-az-subnet-x-001' + } + { + name: '<>-az-pdnsout-x-002' + subnetId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-002/subnets/<>-az-subnet-x-002' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the Private DNS Resolver. | +| `resourceGroupName` | string | The resource group the Private DNS Resolver was deployed into. | +| `resourceId` | string | The resource ID of the Private DNS Resolver. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module dnsResolvers './Microsoft.Network/dnsResolvers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ndrcom' + params: { + // Required parameters + name: '<>ndrcom001' + virtualNetworkId: '' + // Non-required parameters + enableDefaultTelemetry: '' + inboundEndpoints: [ + { + name: '<>-az-pdnsin-x-001' + subnetId: '' + } + ] + outboundEndpoints: [ + { + name: '<>-az-pdnsout-x-001' + subnetId: '' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>ndrcom001" + }, + "virtualNetworkId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "inboundEndpoints": { + "value": [ + { + "name": "<>-az-pdnsin-x-001", + "subnetId": "" + } + ] + }, + "outboundEndpoints": { + "value": [ + { + "name": "<>-az-pdnsout-x-001", + "subnetId": "" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/dnsResolvers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..cb466dbfb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource expressRouteCircuits 'Microsoft.Network/expressRouteCircuits@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(expressRouteCircuits.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: expressRouteCircuits +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/deploy.test.bicep new file mode 100644 index 000000000..3c75f50fb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/common/deploy.test.bicep @@ -0,0 +1,89 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.expressroutecircuits-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nerccom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + bandwidthInMbps: 50 + peeringLocation: 'Amsterdam' + serviceProviderName: 'Equinix' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + skuFamily: 'MeteredData' + skuTier: 'Standard' + allowClassicOperations: true + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/min/deploy.test.bicep new file mode 100644 index 000000000..1a0385336 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/.test/min/deploy.test.bicep @@ -0,0 +1,45 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.expressroutecircuits-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nercmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + bandwidthInMbps: 50 + peeringLocation: 'Amsterdam' + serviceProviderName: 'Equinix' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/deploy.bicep new file mode 100644 index 000000000..93efc78d9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/deploy.bicep @@ -0,0 +1,251 @@ +@description('Required. This is the name of the ExpressRoute circuit.') +param name string + +@description('Required. This is the name of the ExpressRoute Service Provider. It must exactly match one of the Service Providers from List ExpressRoute Service Providers API call.') +param serviceProviderName string + +@description('Required. This is the name of the peering location and not the ARM resource location. It must exactly match one of the available peering locations from List ExpressRoute Service Providers API call.') +param peeringLocation string + +@description('Required. This is the bandwidth in Mbps of the circuit being created. It must exactly match one of the available bandwidth offers List ExpressRoute Service Providers API call.') +param bandwidthInMbps int + +@description('Optional. Chosen SKU Tier of ExpressRoute circuit. Choose from Local, Premium or Standard SKU tiers.') +@allowed([ + 'Local' + 'Standard' + 'Premium' +]) +param skuTier string = 'Standard' + +@description('Optional. Chosen SKU family of ExpressRoute circuit. Choose from MeteredData or UnlimitedData SKU families.') +@allowed([ + 'MeteredData' + 'UnlimitedData' +]) +param skuFamily string = 'MeteredData' + +@description('Optional. Enabled BGP peering type for the Circuit.') +param peering bool = false + +@description('Optional. BGP peering type for the Circuit. Choose from AzurePrivatePeering, AzurePublicPeering or MicrosoftPeering.') +@allowed([ + 'AzurePrivatePeering' + 'MicrosoftPeering' +]) +param peeringType string = 'AzurePrivatePeering' + +@description('Optional. The shared key for peering configuration. Router does MD5 hash comparison to validate the packets sent by BGP connection. This parameter is optional and can be removed from peering configuration if not required.') +param sharedKey string = '' + +@description('Optional. The autonomous system number of the customer/connectivity provider.') +param peerASN int = 0 + +@description('Optional. A /30 subnet used to configure IP addresses for interfaces on Link1.') +param primaryPeerAddressPrefix string = '' + +@description('Optional. A /30 subnet used to configure IP addresses for interfaces on Link2.') +param secondaryPeerAddressPrefix string = '' + +@description('Optional. Specifies the identifier that is used to identify the customer.') +param vlanId int = 0 + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Allow classic operations. You can connect to virtual networks in the classic deployment model by setting allowClassicOperations to true.') +param allowClassicOperations bool = false + +@description('Optional. The bandwidth of the circuit when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct. Default value of 0 will set the property to null.') +param bandwidthInGbps int = 0 + +@description('Optional. The reference to the ExpressRoutePort resource when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct.') +param expressRoutePortResourceId string = '' + +@description('Optional. Flag denoting global reach status. To enable ExpressRoute Global Reach between different geopolitical regions, your circuits must be Premium SKU.') +param globalReachEnabled bool = false + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'PeeringRouteLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var peeringConfiguration = [ + { + name: peeringType + properties: { + peeringType: peeringType + sharedKey: sharedKey + peerASN: peerASN + primaryPeerAddressPrefix: primaryPeerAddressPrefix + secondaryPeerAddressPrefix: secondaryPeerAddressPrefix + vlanId: vlanId + } + } +] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource expressRouteCircuits 'Microsoft.Network/expressRouteCircuits@2022-07-01' = { + name: name + location: location + tags: tags + sku: { + name: '${skuTier}_${skuFamily}' + tier: skuTier + family: skuTier == 'Local' ? 'UnlimitedData' : skuFamily + } + properties: { + allowClassicOperations: allowClassicOperations + globalReachEnabled: globalReachEnabled + bandwidthInGbps: bandwidthInGbps != 0 ? bandwidthInGbps : null + expressRoutePort: !empty(expressRoutePortResourceId) ? { + id: expressRoutePortResourceId + } : null + serviceProviderProperties: { + serviceProviderName: serviceProviderName + peeringLocation: peeringLocation + bandwidthInMbps: bandwidthInMbps + } + peerings: peering ? peeringConfiguration : null + } +} + +resource expressRouteCircuits_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${expressRouteCircuits.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: expressRouteCircuits +} + +resource expressRouteCircuits_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: expressRouteCircuits +} + +module expressRouteCircuits_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-ExpRouteCircuits-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: expressRouteCircuits.id + } +}] + +@description('The resource ID of express route curcuit.') +output resourceId string = expressRouteCircuits.id + +@description('The resource group the express route curcuit was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of express route curcuit.') +output name string = expressRouteCircuits.name + +@description('The service key of the express route circuit.') +output serviceKey string = reference(expressRouteCircuits.id, '2021-02-01').serviceKey + +@description('The location the resource was deployed into.') +output location string = expressRouteCircuits.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/readme.md new file mode 100644 index 000000000..ed9bb9efd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/readme.md @@ -0,0 +1,364 @@ +# ExpressRoute Circuits `[Microsoft.Network/expressRouteCircuits]` + +This template deploys an express route circuit. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/expressRouteCircuits` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/expressRouteCircuits) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `bandwidthInMbps` | int | This is the bandwidth in Mbps of the circuit being created. It must exactly match one of the available bandwidth offers List ExpressRoute Service Providers API call. | +| `name` | string | This is the name of the ExpressRoute circuit. | +| `peeringLocation` | string | This is the name of the peering location and not the ARM resource location. It must exactly match one of the available peering locations from List ExpressRoute Service Providers API call. | +| `serviceProviderName` | string | This is the name of the ExpressRoute Service Provider. It must exactly match one of the Service Providers from List ExpressRoute Service Providers API call. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowClassicOperations` | bool | `False` | | Allow classic operations. You can connect to virtual networks in the classic deployment model by setting allowClassicOperations to true. | +| `bandwidthInGbps` | int | `0` | | The bandwidth of the circuit when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct. Default value of 0 will set the property to null. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, PeeringRouteLog]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `expressRoutePortResourceId` | string | `''` | | The reference to the ExpressRoutePort resource when the circuit is provisioned on an ExpressRoutePort resource. Available when configuring Express Route Direct. | +| `globalReachEnabled` | bool | `False` | | Flag denoting global reach status. To enable ExpressRoute Global Reach between different geopolitical regions, your circuits must be Premium SKU. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `peerASN` | int | `0` | | The autonomous system number of the customer/connectivity provider. | +| `peering` | bool | `False` | | Enabled BGP peering type for the Circuit. | +| `peeringType` | string | `'AzurePrivatePeering'` | `[AzurePrivatePeering, MicrosoftPeering]` | BGP peering type for the Circuit. Choose from AzurePrivatePeering, AzurePublicPeering or MicrosoftPeering. | +| `primaryPeerAddressPrefix` | string | `''` | | A /30 subnet used to configure IP addresses for interfaces on Link1. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `secondaryPeerAddressPrefix` | string | `''` | | A /30 subnet used to configure IP addresses for interfaces on Link2. | +| `sharedKey` | string | `''` | | The shared key for peering configuration. Router does MD5 hash comparison to validate the packets sent by BGP connection. This parameter is optional and can be removed from peering configuration if not required. | +| `skuFamily` | string | `'MeteredData'` | `[MeteredData, UnlimitedData]` | Chosen SKU family of ExpressRoute circuit. Choose from MeteredData or UnlimitedData SKU families. | +| `skuTier` | string | `'Standard'` | `[Local, Premium, Standard]` | Chosen SKU Tier of ExpressRoute circuit. Choose from Local, Premium or Standard SKU tiers. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `vlanId` | int | `0` | | Specifies the identifier that is used to identify the customer. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of express route curcuit. | +| `resourceGroupName` | string | The resource group the express route curcuit was deployed into. | +| `resourceId` | string | The resource ID of express route curcuit. | +| `serviceKey` | string | The service key of the express route circuit. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module expressRouteCircuits './Microsoft.Network/expressRouteCircuits/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nerccom' + params: { + // Required parameters + bandwidthInMbps: 50 + name: '<>nerccom001' + peeringLocation: 'Amsterdam' + serviceProviderName: 'Equinix' + // Non-required parameters + allowClassicOperations: true + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + skuFamily: 'MeteredData' + skuTier: 'Standard' + tags: { + Environment: 'Non-Prod' + 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 + "bandwidthInMbps": { + "value": 50 + }, + "name": { + "value": "<>nerccom001" + }, + "peeringLocation": { + "value": "Amsterdam" + }, + "serviceProviderName": { + "value": "Equinix" + }, + // Non-required parameters + "allowClassicOperations": { + "value": true + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuFamily": { + "value": "MeteredData" + }, + "skuTier": { + "value": "Standard" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module expressRouteCircuits './Microsoft.Network/expressRouteCircuits/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nercmin' + params: { + // Required parameters + bandwidthInMbps: 50 + name: '<>nercmin001' + peeringLocation: 'Amsterdam' + serviceProviderName: 'Equinix' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "bandwidthInMbps": { + "value": 50 + }, + "name": { + "value": "<>nercmin001" + }, + "peeringLocation": { + "value": "Amsterdam" + }, + "serviceProviderName": { + "value": "Equinix" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteCircuits/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..2af219017 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource applicationGateway 'Microsoft.Network/applicationGateways@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(applicationGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: applicationGateway +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/dependencies.bicep new file mode 100644 index 000000000..c865cb691 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/dependencies.bicep @@ -0,0 +1,26 @@ +@description('Required. The name of the virtual WAN to create.') +param virtualWANName string + +@description('Required. The name of the virtual Hub to create.') +param virtualHubName string +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-07-01' = { + name: virtualHubName + location: location + properties: { + addressPrefix: '10.0.0.0/16' + virtualWan: { + id: virtualWan.id + } + } +} + +@description('The resource ID of the created Virtual Hub.') +output virtualHubResourceId string = virtualHub.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/deploy.test.bicep new file mode 100644 index 000000000..6503ddcde --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/.test/common/deploy.test.bicep @@ -0,0 +1,56 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.expressRouteGateway-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nergcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualWANName: 'dep-<>-vwan-${serviceShort}' + virtualHubName: 'dep-<>-hub-${serviceShort}' + } +} +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + tags: { + hello: 'world' + } + autoScaleConfigurationBoundsMin: 2 + autoScaleConfigurationBoundsMax: 3 + virtualHubId: nestedDependencies.outputs.virtualHubResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/deploy.bicep new file mode 100644 index 000000000..effbdf4f8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/deploy.bicep @@ -0,0 +1,69 @@ +@description('Required. Name of the Express Route Gateway.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Firewall policy resource.') +param tags object = {} + +@description('Optional. Configures this gateway to accept traffic from non Virtual WAN networks.') +param allowNonVirtualWanTraffic bool = false + +@description('Optional. Maximum number of scale units deployed for ExpressRoute gateway.') +param autoScaleConfigurationBoundsMax int = 2 + +@description('Optional. Minimum number of scale units deployed for ExpressRoute gateway.') +param autoScaleConfigurationBoundsMin int = 2 + +@description('Optional. List of ExpressRoute connections to the ExpressRoute gateway.') +param expressRouteConnections array = [] + +@description('Required. Resource ID of the Virtual Wan Hub.') +param virtualHubId string + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource expressRouteGateway 'Microsoft.Network/expressRouteGateways@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + allowNonVirtualWanTraffic: allowNonVirtualWanTraffic + autoScaleConfiguration: { + bounds: { + max: autoScaleConfigurationBoundsMax + min: autoScaleConfigurationBoundsMin + } + } + expressRouteConnections: expressRouteConnections + virtualHub: { + id: virtualHubId + } + } +} + +@description('The resource ID of the ExpressRoute Gateway.') +output resourceId string = expressRouteGateway.id + +@description('The resource group of the ExpressRoute Gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the ExpressRoute Gateway.') +output name string = expressRouteGateway.name + +@description('The location the resource was deployed into.') +output location string = expressRouteGateway.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/readme.md new file mode 100644 index 000000000..3b2d8c74b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/readme.md @@ -0,0 +1,257 @@ +# Express Route Gateways `[Microsoft.Network/expressRouteGateway]` + +This module deploys Expess Route Gateways. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/expressRouteGateways` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/expressRouteGateways) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Express Route Gateway. | +| `virtualHubId` | string | Resource ID of the Virtual Wan Hub. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `allowNonVirtualWanTraffic` | bool | `False` | Configures this gateway to accept traffic from non Virtual WAN networks. | +| `autoScaleConfigurationBoundsMax` | int | `2` | Maximum number of scale units deployed for ExpressRoute gateway. | +| `autoScaleConfigurationBoundsMin` | int | `2` | Minimum number of scale units deployed for ExpressRoute gateway. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `expressRouteConnections` | array | `[]` | List of ExpressRoute connections to the ExpressRoute gateway. | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `tags` | object | `{object}` | Tags of the Firewall policy resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the ExpressRoute Gateway. | +| `resourceGroupName` | string | The resource group of the ExpressRoute Gateway was deployed into. | +| `resourceId` | string | The resource ID of the ExpressRoute Gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module expressRouteGateway './Microsoft.Network/expressRouteGateway/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nergcom' + params: { + // Required parameters + name: '<>nergcom001' + virtualHubId: '' + // Non-required parameters + autoScaleConfigurationBoundsMax: 3 + autoScaleConfigurationBoundsMin: 2 + enableDefaultTelemetry: '' + tags: { + hello: 'world' + } + } +} +``` + +
+

+ +

+ +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": "<>nergcom001" + }, + "virtualHubId": { + "value": "" + }, + // Non-required parameters + "autoScaleConfigurationBoundsMax": { + "value": 3 + }, + "autoScaleConfigurationBoundsMin": { + "value": 2 + }, + "enableDefaultTelemetry": { + "value": "" + }, + "tags": { + "value": { + "hello": "world" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/expressRouteGateway/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/common/deploy.test.bicep new file mode 100644 index 000000000..d70aa6ce4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/common/deploy.test.bicep @@ -0,0 +1,86 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.firewallpolicies-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nfpcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + ruleCollectionGroups: [ + { + name: '<>-rule-001' + priority: 5000 + ruleCollections: [ + { + action: { + type: 'Allow' + } + name: 'collection002' + priority: 5555 + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + rules: [ + { + destinationAddresses: [ + '*' + ] + destinationFqdns: [] + destinationIpGroups: [] + destinationPorts: [ + '80' + ] + ipProtocols: [ + 'TCP' + 'UDP' + ] + name: 'rule002' + ruleType: 'NetworkRule' + sourceAddresses: [ + '*' + ] + sourceIpGroups: [] + } + ] + } + ] + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + allowSqlRedirect: true + autoLearnPrivateRanges: 'Enabled' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/min/deploy.test.bicep new file mode 100644 index 000000000..a279cea18 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.firewallpolicies-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nfpmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/deploy.bicep new file mode 100644 index 000000000..3e893fd98 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/deploy.bicep @@ -0,0 +1,199 @@ +@description('Required. Name of the Firewall Policy.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Firewall policy resource.') +param tags object = {} + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Resource ID of the base policy.') +param basePolicyResourceId string = '' + +@description('Optional. Enable DNS Proxy on Firewalls attached to the Firewall Policy.') +param enableProxy bool = false + +@description('Optional. List of Custom DNS Servers.') +param servers array = [] + +@description('Optional. A flag to indicate if the insights are enabled on the policy.') +param insightsIsEnabled bool = false + +@description('Optional. Default Log Analytics Resource ID for Firewall Policy Insights.') +param defaultWorkspaceId string = '' + +@description('Optional. List of workspaces for Firewall Policy Insights.') +param workspaces array = [] + +@description('Optional. Number of days the insights should be enabled on the policy.') +param retentionDays int = 365 + +@description('Optional. List of rules for traffic to bypass.') +param bypassTrafficSettings array = [] + +@description('Optional. List of specific signatures states.') +param signatureOverrides array = [] + +@description('Optional. The configuring of intrusion detection.') +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param mode string = 'Off' + +@description('Optional. Tier of Firewall Policy.') +@allowed([ + 'Premium' + 'Standard' +]) +param tier string = 'Standard' + +@description('Optional. List of private IP addresses/IP address ranges to not be SNAT.') +param privateRanges array = [] + +@allowed([ + 'Disabled' + 'Enabled' +]) +@description('Optional. The operation mode for automatically learning private ranges to not be SNAT.') +param autoLearnPrivateRanges string = 'Disabled' + +@description('Optional. The operation mode for Threat Intel.') +@allowed([ + 'Alert' + 'Deny' + 'Off' +]) +param threatIntelMode string = 'Off' + +@description('Optional. A flag to indicate if SQL Redirect traffic filtering is enabled. Turning on the flag requires no rule using port 11000-11999.') +param allowSqlRedirect bool = false + +@description('Optional. List of FQDNs for the ThreatIntel Allowlist.') +param fqdns array = [] + +@description('Optional. List of IP addresses for the ThreatIntel Allowlist.') +param ipAddresses array = [] + +@description('Optional. Secret ID of (base-64 encoded unencrypted PFX) Secret or Certificate object stored in KeyVault.') +#disable-next-line secure-secrets-in-params // Not a secret +param keyVaultSecretId string = '' + +@description('Optional. Name of the CA certificate.') +param certificateName string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Rule collection groups.') +param ruleCollectionGroups array = [] + +var identityType = systemAssignedIdentity ? (!empty(userAssignedIdentities) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource firewallPolicy 'Microsoft.Network/firewallPolicies@2022-07-01' = { + name: name + location: location + tags: tags + identity: identity + properties: { + basePolicy: !empty(basePolicyResourceId) ? { + id: basePolicyResourceId + } : null + dnsSettings: enableProxy ? { + enableProxy: enableProxy + servers: servers + } : null + insights: insightsIsEnabled ? { + isEnabled: insightsIsEnabled + logAnalyticsResources: { + defaultWorkspaceId: { + id: !empty(defaultWorkspaceId) ? defaultWorkspaceId : null + } + workspaces: !empty(workspaces) ? workspaces : null + } + retentionDays: retentionDays + } : null + intrusionDetection: (mode != 'Off') ? { + configuration: { + bypassTrafficSettings: !empty(bypassTrafficSettings) ? bypassTrafficSettings : null + signatureOverrides: !empty(signatureOverrides) ? signatureOverrides : null + } + mode: mode + } : null + sku: { + tier: tier + } + snat: !empty(privateRanges) ? { + autoLearnPrivateRanges: autoLearnPrivateRanges + privateRanges: privateRanges + } : null + sql: { + allowSqlRedirect: allowSqlRedirect + } + threatIntelMode: threatIntelMode + threatIntelWhitelist: { + fqdns: fqdns + ipAddresses: ipAddresses + } + transportSecurity: (!empty(keyVaultSecretId) || !empty(certificateName)) ? { + certificateAuthority: { + keyVaultSecretId: !empty(keyVaultSecretId) ? keyVaultSecretId : null + name: !empty(certificateName) ? certificateName : null + } + } : null + } +} + +// When a FW policy uses a base policy and have more rule collection groups, +// they need to be deployed sequentially, otherwise the deployment would fail +// because of concurrent access to the base policy. +// The next line forces ARM to deploy them one after the other, so no race concition on the base policy will happen. +@batchSize(1) +module firewallPolicy_ruleCollectionGroups 'ruleCollectionGroups/deploy.bicep' = [for (ruleCollectionGroup, index) in ruleCollectionGroups: { + name: '${uniqueString(deployment().name, location)}-firewallPolicy_ruleCollectionGroups-${index}' + params: { + firewallPolicyName: firewallPolicy.name + name: ruleCollectionGroup.name + priority: ruleCollectionGroup.priority + ruleCollections: ruleCollectionGroup.ruleCollections + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the deployed firewall policy.') +output name string = firewallPolicy.name + +@description('The resource ID of the deployed firewall policy.') +output resourceId string = firewallPolicy.id + +@description('The resource group of the deployed firewall policy.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = firewallPolicy.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/readme.md new file mode 100644 index 000000000..91c9f2292 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/readme.md @@ -0,0 +1,337 @@ +# Firewall Policies `[Microsoft.Network/firewallPolicies]` + +This module deploys Firewall Policies. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/firewallPolicies` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/firewallPolicies) | +| `Microsoft.Network/firewallPolicies/ruleCollectionGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/firewallPolicies/ruleCollectionGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Firewall Policy. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowSqlRedirect` | bool | `False` | | A flag to indicate if SQL Redirect traffic filtering is enabled. Turning on the flag requires no rule using port 11000-11999. | +| `autoLearnPrivateRanges` | string | `'Disabled'` | `[Disabled, Enabled]` | The operation mode for automatically learning private ranges to not be SNAT. | +| `basePolicyResourceId` | string | `''` | | Resource ID of the base policy. | +| `bypassTrafficSettings` | array | `[]` | | List of rules for traffic to bypass. | +| `certificateName` | string | `''` | | Name of the CA certificate. | +| `defaultWorkspaceId` | string | `''` | | Default Log Analytics Resource ID for Firewall Policy Insights. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableProxy` | bool | `False` | | Enable DNS Proxy on Firewalls attached to the Firewall Policy. | +| `fqdns` | array | `[]` | | List of FQDNs for the ThreatIntel Allowlist. | +| `insightsIsEnabled` | bool | `False` | | A flag to indicate if the insights are enabled on the policy. | +| `ipAddresses` | array | `[]` | | List of IP addresses for the ThreatIntel Allowlist. | +| `keyVaultSecretId` | string | `''` | | Secret ID of (base-64 encoded unencrypted PFX) Secret or Certificate object stored in KeyVault. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `mode` | string | `'Off'` | `[Alert, Deny, Off]` | The configuring of intrusion detection. | +| `privateRanges` | array | `[]` | | List of private IP addresses/IP address ranges to not be SNAT. | +| `retentionDays` | int | `365` | | Number of days the insights should be enabled on the policy. | +| `ruleCollectionGroups` | _[ruleCollectionGroups](ruleCollectionGroups/readme.md)_ array | `[]` | | Rule collection groups. | +| `servers` | array | `[]` | | List of Custom DNS Servers. | +| `signatureOverrides` | array | `[]` | | List of specific signatures states. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the Firewall policy resource. | +| `threatIntelMode` | string | `'Off'` | `[Alert, Deny, Off]` | The operation mode for Threat Intel. | +| `tier` | string | `'Standard'` | `[Premium, Standard]` | Tier of Firewall Policy. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `workspaces` | array | `[]` | | List of workspaces for Firewall Policy Insights. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed firewall policy. | +| `resourceGroupName` | string | The resource group of the deployed firewall policy. | +| `resourceId` | string | The resource ID of the deployed firewall policy. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module firewallPolicies './Microsoft.Network/firewallPolicies/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nfpcom' + params: { + // Required parameters + name: '<>nfpcom001' + // Non-required parameters + allowSqlRedirect: true + autoLearnPrivateRanges: 'Enabled' + enableDefaultTelemetry: '' + ruleCollectionGroups: [ + { + name: '<>-rule-001' + priority: 5000 + ruleCollections: [ + { + action: { + type: 'Allow' + } + name: 'collection002' + priority: 5555 + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + rules: [ + { + destinationAddresses: [ + '*' + ] + destinationFqdns: [] + destinationIpGroups: [] + destinationPorts: [ + '80' + ] + ipProtocols: [ + 'TCP' + 'UDP' + ] + name: 'rule002' + ruleType: 'NetworkRule' + sourceAddresses: [ + '*' + ] + sourceIpGroups: [] + } + ] + } + ] + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nfpcom001" + }, + // Non-required parameters + "allowSqlRedirect": { + "value": true + }, + "autoLearnPrivateRanges": { + "value": "Enabled" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "ruleCollectionGroups": { + "value": [ + { + "name": "<>-rule-001", + "priority": 5000, + "ruleCollections": [ + { + "action": { + "type": "Allow" + }, + "name": "collection002", + "priority": 5555, + "ruleCollectionType": "FirewallPolicyFilterRuleCollection", + "rules": [ + { + "destinationAddresses": [ + "*" + ], + "destinationFqdns": [], + "destinationIpGroups": [], + "destinationPorts": [ + "80" + ], + "ipProtocols": [ + "TCP", + "UDP" + ], + "name": "rule002", + "ruleType": "NetworkRule", + "sourceAddresses": [ + "*" + ], + "sourceIpGroups": [] + } + ] + } + ] + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module firewallPolicies './Microsoft.Network/firewallPolicies/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nfpmin' + params: { + // Required parameters + name: '<>nfpmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nfpmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep new file mode 100644 index 000000000..8932b2007 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/deploy.bicep @@ -0,0 +1,48 @@ +@description('Conditional. The name of the parent Firewall Policy. Required if the template is used in a standalone deployment.') +param firewallPolicyName string + +@description('Required. The name of the rule collection group to deploy.') +param name string + +@description('Required. Priority of the Firewall Policy Rule Collection Group resource.') +param priority int + +@description('Optional. Group of Firewall Policy rule collections.') +param ruleCollections array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource firewallPolicy 'Microsoft.Network/firewallPolicies@2022-07-01' existing = { + name: firewallPolicyName +} + +resource ruleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-07-01' = { + name: name + parent: firewallPolicy + properties: { + priority: priority + ruleCollections: ruleCollections + } +} + +@description('The name of the deployed rule collection group.') +output name string = ruleCollectionGroup.name + +@description('The resource ID of the deployed rule collection group.') +output resourceId string = ruleCollectionGroup.id + +@description('The resource group of the deployed rule collection group.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md new file mode 100644 index 000000000..2d60f41ad --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/readme.md @@ -0,0 +1,51 @@ +# Network Firewall Policies Rule Collection Groups `[Microsoft.Network/firewallPolicies/ruleCollectionGroups]` + +This module deploys Network Firewall Policies Rule Collection Groups. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/firewallPolicies/ruleCollectionGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/firewallPolicies/ruleCollectionGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the rule collection group to deploy. | +| `priority` | int | Priority of the Firewall Policy Rule Collection Group resource. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `firewallPolicyName` | string | The name of the parent Firewall Policy. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ruleCollections` | array | `[]` | Group of Firewall Policy rule collections. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed rule collection group. | +| `resourceGroupName` | string | The resource group of the deployed rule collection group. | +| `resourceId` | string | The resource ID of the deployed rule collection group. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/ruleCollectionGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/firewallPolicies/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..2090906dd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource frontDoor 'Microsoft.Network/frontDoors@2020-05-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(frontDoor.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: frontDoor +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/deploy.test.bicep new file mode 100644 index 000000000..cacb34a80 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/common/deploy.test.bicep @@ -0,0 +1,153 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.frontdoors-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nfdcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // +var resourceName = '<>${serviceShort}001' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: resourceName + backendPools: [ + { + name: 'backendPool' + properties: { + backends: [ + { + address: 'biceptest.local' + backendHostHeader: 'backendAddress' + enabledState: 'Enabled' + httpPort: 80 + httpsPort: 443 + priority: 1 + privateLinkAlias: '' + privateLinkApprovalMessage: '' + privateLinkLocation: '' + privateLinkResourceId: '' + weight: 50 + } + ] + HealthProbeSettings: { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/HealthProbeSettings/heathProbe' + } + LoadBalancingSettings: { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/LoadBalancingSettings/loadBalancer' + } + } + } + ] + enforceCertificateNameCheck: 'Disabled' + frontendEndpoints: [ + { + name: 'frontEnd' + properties: { + hostName: '${resourceName}.${environment().suffixes.azureFrontDoorEndpointSuffix}' + sessionAffinityEnabledState: 'Disabled' + sessionAffinityTtlSeconds: 60 + } + } + ] + healthProbeSettings: [ + { + name: 'heathProbe' + properties: { + enabledState: '' + healthProbeMethod: '' + intervalInSeconds: 60 + path: '/' + protocol: 'Https' + } + } + ] + loadBalancingSettings: [ + { + name: 'loadBalancer' + properties: { + additionalLatencyMilliseconds: 0 + sampleSize: 50 + successfulSamplesRequired: 1 + } + } + ] + lock: 'CanNotDelete' + routingRules: [ + { + name: 'routingRule' + properties: { + acceptedProtocols: [ + 'Http' + 'Https' + ] + enabledState: 'Enabled' + frontendEndpoints: [ + { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/FrontendEndpoints/frontEnd' + } + ] + patternsToMatch: [ + '/*' + ] + routeConfiguration: { + '@odata.type': '#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration' + backendPool: { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/BackendPools/backendPool' + } + forwardingProtocol: 'MatchRequest' + } + } + } + ] + sendRecvTimeoutSeconds: 10 + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/min/deploy.test.bicep new file mode 100644 index 000000000..a64556800 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/.test/min/deploy.test.bicep @@ -0,0 +1,121 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.frontdoors-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nfdmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // +var resourceName = '<>${serviceShort}001' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: resourceName + frontendEndpoints: [ + { + name: 'frontEnd' + properties: { + hostName: '${resourceName}.${environment().suffixes.azureFrontDoorEndpointSuffix}' + sessionAffinityEnabledState: 'Disabled' + sessionAffinityTtlSeconds: 60 + } + } + ] + healthProbeSettings: [ + { + name: 'heathProbe' + properties: { + intervalInSeconds: 60 + path: '/' + protocol: 'Https' + } + } + ] + loadBalancingSettings: [ + { + name: 'loadBalancer' + properties: { + additionalLatencyMilliseconds: 0 + sampleSize: 50 + successfulSamplesRequired: 1 + } + } + ] + routingRules: [ + { + name: 'routingRule' + properties: { + acceptedProtocols: [ + 'Https' + ] + enabledState: 'Enabled' + frontendEndpoints: [ + { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/FrontendEndpoints/frontEnd' + } + ] + patternsToMatch: [ + '/*' + ] + routeConfiguration: { + '@odata.type': '#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration' + backendPool: { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/BackendPools/backendPool' + } + } + } + } + ] + backendPools: [ + { + name: 'backendPool' + properties: { + backends: [ + { + address: 'biceptest.local' + backendHostHeader: 'backendAddress' + enabledState: 'Enabled' + httpPort: 80 + httpsPort: 443 + priority: 1 + weight: 50 + } + ] + HealthProbeSettings: { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/HealthProbeSettings/heathProbe' + } + LoadBalancingSettings: { + id: '${resourceGroup.id}/providers/Microsoft.Network/frontDoors/${resourceName}/LoadBalancingSettings/loadBalancer' + } + } + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/deploy.bicep new file mode 100644 index 000000000..169cb4266 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/deploy.bicep @@ -0,0 +1,192 @@ +@description('Required. The name of the frontDoor.') +@minLength(1) +@maxLength(64) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Required. Backend address pool of the frontdoor resource.') +param backendPools array + +@description('Optional. Enforce certificate name check of the frontdoor resource.') +param enforceCertificateNameCheck string = 'Disabled' + +@description('Optional. Certificate name check time of the frontdoor resource.') +@maxValue(240) +param sendRecvTimeoutSeconds int = 240 + +@description('Optional. State of the frontdoor resource.') +param enabledState string = 'Enabled' + +@description('Optional. Friendly name of the frontdoor resource.') +param friendlyName string = '' + +@description('Required. Frontend endpoints of the frontdoor resource.') +param frontendEndpoints array + +@description('Required. Heath probe settings of the frontdoor resource.') +param healthProbeSettings array + +@description('Required. Load balancing settings of the frontdoor resource.') +param loadBalancingSettings array + +@description('Required. Routing rules settings of the frontdoor resource.') +param routingRules array + +@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 diagnosticLogsRetentionInDays int = 365 + +@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.') +param diagnosticStorageAccountId string = '' + +@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.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'FrontdoorAccessLog' + 'FrontdoorWebApplicationFirewallLog' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param metricsToEnable array = [ + 'AllMetrics' +] + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in metricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource frontDoor 'Microsoft.Network/frontDoors@2020-05-01' = { + name: name + location: 'global' + tags: tags + properties: { + backendPools: backendPools + backendPoolsSettings: { + enforceCertificateNameCheck: enforceCertificateNameCheck + sendRecvTimeoutSeconds: sendRecvTimeoutSeconds + } + enabledState: enabledState + friendlyName: friendlyName + frontendEndpoints: frontendEndpoints + healthProbeSettings: healthProbeSettings + loadBalancingSettings: loadBalancingSettings + routingRules: routingRules + } +} + +resource frontDoor_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${frontDoor.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: frontDoor +} + +resource frontDoor_diagnosticSettingName 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: '${frontDoor.name}-diagnosticSettings' + properties: { + storageAccountId: empty(diagnosticStorageAccountId) ? null : diagnosticStorageAccountId + workspaceId: empty(diagnosticWorkspaceId) ? null : diagnosticWorkspaceId + eventHubAuthorizationRuleId: empty(diagnosticEventHubAuthorizationRuleId) ? null : diagnosticEventHubAuthorizationRuleId + eventHubName: empty(diagnosticEventHubName) ? null : diagnosticEventHubName + metrics: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsMetrics + logs: empty(diagnosticStorageAccountId) && empty(diagnosticWorkspaceId) && empty(diagnosticEventHubAuthorizationRuleId) && empty(diagnosticEventHubName) ? null : diagnosticsLogs + } + scope: frontDoor +} + +module frontDoor_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: frontDoor.id + } +}] + +@description('The name of the front door.') +output name string = frontDoor.name + +@description('The resource ID of the front door.') +output resourceId string = frontDoor.id + +@description('The resource group the front door was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/readme.md new file mode 100644 index 000000000..258574f84 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/readme.md @@ -0,0 +1,659 @@ +# Front Doors `[Microsoft.Network/frontDoors]` + +This module deploys Front Doors. + + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/frontDoors` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-05-01/frontDoors) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `backendPools` | array | Backend address pool of the frontdoor resource. | +| `frontendEndpoints` | array | Frontend endpoints of the frontdoor resource. | +| `healthProbeSettings` | array | Heath probe settings of the frontdoor resource. | +| `loadBalancingSettings` | array | Load balancing settings of the frontdoor resource. | +| `name` | string | The name of the frontDoor. | +| `routingRules` | array | Routing rules settings of the frontdoor resource. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, FrontdoorAccessLog, FrontdoorWebApplicationFirewallLog]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticStorageAccountId` | 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. | +| `diagnosticWorkspaceId` | 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. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enabledState` | string | `'Enabled'` | | State of the frontdoor resource. | +| `enforceCertificateNameCheck` | string | `'Disabled'` | | Enforce certificate name check of the frontdoor resource. | +| `friendlyName` | string | `''` | | Friendly name of the frontdoor resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `metricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `sendRecvTimeoutSeconds` | int | `240` | | Certificate name check time of the frontdoor resource. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the front door. | +| `resourceGroupName` | string | The resource group the front door was deployed into. | +| `resourceId` | string | The resource ID of the front door. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module frontDoors './Microsoft.Network/frontDoors/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nfdcom' + params: { + // Required parameters + backendPools: [ + { + name: 'backendPool' + properties: { + backends: [ + { + address: 'biceptest.local' + backendHostHeader: 'backendAddress' + enabledState: 'Enabled' + httpPort: 80 + httpsPort: 443 + priority: 1 + privateLinkAlias: '' + privateLinkApprovalMessage: '' + privateLinkLocation: '' + privateLinkResourceId: '' + weight: 50 + } + ] + HealthProbeSettings: { + id: '' + } + LoadBalancingSettings: { + id: '' + } + } + } + ] + frontendEndpoints: [ + { + name: 'frontEnd' + properties: { + hostName: '' + sessionAffinityEnabledState: 'Disabled' + sessionAffinityTtlSeconds: 60 + } + } + ] + healthProbeSettings: [ + { + name: 'heathProbe' + properties: { + enabledState: '' + healthProbeMethod: '' + intervalInSeconds: 60 + path: '/' + protocol: 'Https' + } + } + ] + loadBalancingSettings: [ + { + name: 'loadBalancer' + properties: { + additionalLatencyMilliseconds: 0 + sampleSize: 50 + successfulSamplesRequired: 1 + } + } + ] + name: '' + routingRules: [ + { + name: 'routingRule' + properties: { + acceptedProtocols: [ + 'Http' + 'Https' + ] + enabledState: 'Enabled' + frontendEndpoints: [ + { + id: '' + } + ] + patternsToMatch: [ + '/*' + ] + routeConfiguration: { + '@odata.type': '#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration' + backendPool: { + id: '' + } + forwardingProtocol: 'MatchRequest' + } + } + } + ] + // Non-required parameters + enableDefaultTelemetry: '' + enforceCertificateNameCheck: 'Disabled' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + sendRecvTimeoutSeconds: 10 + tags: { + Environment: 'Non-Prod' + 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 + "backendPools": { + "value": [ + { + "name": "backendPool", + "properties": { + "backends": [ + { + "address": "biceptest.local", + "backendHostHeader": "backendAddress", + "enabledState": "Enabled", + "httpPort": 80, + "httpsPort": 443, + "priority": 1, + "privateLinkAlias": "", + "privateLinkApprovalMessage": "", + "privateLinkLocation": "", + "privateLinkResourceId": "", + "weight": 50 + } + ], + "HealthProbeSettings": { + "id": "" + }, + "LoadBalancingSettings": { + "id": "" + } + } + } + ] + }, + "frontendEndpoints": { + "value": [ + { + "name": "frontEnd", + "properties": { + "hostName": "", + "sessionAffinityEnabledState": "Disabled", + "sessionAffinityTtlSeconds": 60 + } + } + ] + }, + "healthProbeSettings": { + "value": [ + { + "name": "heathProbe", + "properties": { + "enabledState": "", + "healthProbeMethod": "", + "intervalInSeconds": 60, + "path": "/", + "protocol": "Https" + } + } + ] + }, + "loadBalancingSettings": { + "value": [ + { + "name": "loadBalancer", + "properties": { + "additionalLatencyMilliseconds": 0, + "sampleSize": 50, + "successfulSamplesRequired": 1 + } + } + ] + }, + "name": { + "value": "" + }, + "routingRules": { + "value": [ + { + "name": "routingRule", + "properties": { + "acceptedProtocols": [ + "Http", + "Https" + ], + "enabledState": "Enabled", + "frontendEndpoints": [ + { + "id": "" + } + ], + "patternsToMatch": [ + "/*" + ], + "routeConfiguration": { + "@odata.type": "#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration", + "backendPool": { + "id": "" + }, + "forwardingProtocol": "MatchRequest" + } + } + } + ] + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "enforceCertificateNameCheck": { + "value": "Disabled" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "sendRecvTimeoutSeconds": { + "value": 10 + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module frontDoors './Microsoft.Network/frontDoors/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nfdmin' + params: { + // Required parameters + backendPools: [ + { + name: 'backendPool' + properties: { + backends: [ + { + address: 'biceptest.local' + backendHostHeader: 'backendAddress' + enabledState: 'Enabled' + httpPort: 80 + httpsPort: 443 + priority: 1 + weight: 50 + } + ] + HealthProbeSettings: { + id: '' + } + LoadBalancingSettings: { + id: '' + } + } + } + ] + frontendEndpoints: [ + { + name: 'frontEnd' + properties: { + hostName: '' + sessionAffinityEnabledState: 'Disabled' + sessionAffinityTtlSeconds: 60 + } + } + ] + healthProbeSettings: [ + { + name: 'heathProbe' + properties: { + intervalInSeconds: 60 + path: '/' + protocol: 'Https' + } + } + ] + loadBalancingSettings: [ + { + name: 'loadBalancer' + properties: { + additionalLatencyMilliseconds: 0 + sampleSize: 50 + successfulSamplesRequired: 1 + } + } + ] + name: '' + routingRules: [ + { + name: 'routingRule' + properties: { + acceptedProtocols: [ + 'Https' + ] + enabledState: 'Enabled' + frontendEndpoints: [ + { + id: '' + } + ] + patternsToMatch: [ + '/*' + ] + routeConfiguration: { + '@odata.type': '#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration' + backendPool: { + id: '' + } + } + } + } + ] + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "backendPools": { + "value": [ + { + "name": "backendPool", + "properties": { + "backends": [ + { + "address": "biceptest.local", + "backendHostHeader": "backendAddress", + "enabledState": "Enabled", + "httpPort": 80, + "httpsPort": 443, + "priority": 1, + "weight": 50 + } + ], + "HealthProbeSettings": { + "id": "" + }, + "LoadBalancingSettings": { + "id": "" + } + } + } + ] + }, + "frontendEndpoints": { + "value": [ + { + "name": "frontEnd", + "properties": { + "hostName": "", + "sessionAffinityEnabledState": "Disabled", + "sessionAffinityTtlSeconds": 60 + } + } + ] + }, + "healthProbeSettings": { + "value": [ + { + "name": "heathProbe", + "properties": { + "intervalInSeconds": 60, + "path": "/", + "protocol": "Https" + } + } + ] + }, + "loadBalancingSettings": { + "value": [ + { + "name": "loadBalancer", + "properties": { + "additionalLatencyMilliseconds": 0, + "sampleSize": 50, + "successfulSamplesRequired": 1 + } + } + ] + }, + "name": { + "value": "" + }, + "routingRules": { + "value": [ + { + "name": "routingRule", + "properties": { + "acceptedProtocols": [ + "Https" + ], + "enabledState": "Enabled", + "frontendEndpoints": [ + { + "id": "" + } + ], + "patternsToMatch": [ + "/*" + ], + "routeConfiguration": { + "@odata.type": "#Microsoft.Azure.FrontDoor.Models.FrontdoorForwardingConfiguration", + "backendPool": { + "id": "" + } + } + } + } + ] + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/frontDoors/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..62ee58272 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource ipGroup 'Microsoft.Network/ipGroups@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(ipGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: ipGroup +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/deploy.test.bicep new file mode 100644 index 000000000..5e8423690 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/common/deploy.test.bicep @@ -0,0 +1,68 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.ipgroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nigcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + ipAddresses: [ + '10.0.0.1' + '10.0.0.2' + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/min/deploy.test.bicep new file mode 100644 index 000000000..81beb4d4c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.ipgroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nigmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/deploy.bicep new file mode 100644 index 000000000..ad81f84c2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/deploy.bicep @@ -0,0 +1,81 @@ +@description('Required. The name of the ipGroups.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. IpAddresses/IpAddressPrefixes in the IpGroups resource.') +param ipAddresses array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource ipGroup 'Microsoft.Network/ipGroups@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + ipAddresses: ipAddresses + } +} + +resource ipGroup_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${ipGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: ipGroup +} + +module ipGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-IPGroup-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: ipGroup.id + } +}] + +@description('The resource ID of the IP group.') +output resourceId string = ipGroup.id + +@description('The resource group of the IP group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the IP group.') +output name string = ipGroup.name + +@description('The location the resource was deployed into.') +output location string = ipGroup.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/readme.md new file mode 100644 index 000000000..11fdb6057 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/readme.md @@ -0,0 +1,293 @@ +# IP Groups `[Microsoft.Network/ipGroups]` + +This module deploys an IP group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/ipGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/ipGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the ipGroups. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ipAddresses` | array | `[]` | | IpAddresses/IpAddressPrefixes in the IpGroups resource. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Resource tags. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the IP group. | +| `resourceGroupName` | string | The resource group of the IP group was deployed into. | +| `resourceId` | string | The resource ID of the IP group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module ipGroups './Microsoft.Network/ipGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nigcom' + params: { + // Required parameters + name: '<>nigcom001' + // Non-required parameters + enableDefaultTelemetry: '' + ipAddresses: [ + '10.0.0.1' + '10.0.0.2' + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nigcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "ipAddresses": { + "value": [ + "10.0.0.1", + "10.0.0.2" + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module ipGroups './Microsoft.Network/ipGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nigmin' + params: { + // Required parameters + name: '<>nigmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nigmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/ipGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..3972bf7c7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(loadBalancer.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: loadBalancer +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/dependencies.bicep new file mode 100644 index 000000000..c435b9706 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/dependencies.bicep @@ -0,0 +1,36 @@ +@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 Managed Identity to create.') +param managedIdentityName string + +resource publicIP 'Microsoft.Network/publicIPAddresses@2022-01-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 +} + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/deploy.test.bicep new file mode 100644 index 000000000..e85699dc2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/common/deploy.test.bicep @@ -0,0 +1,164 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.loadbalancers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nlbcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + publicIPName: 'dep-<>-pip-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + frontendIPConfigurations: [ + { + name: 'publicIPConfig1' + publicIPAddressId: nestedDependencies.outputs.publicIPResourceId + } + ] + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + { + name: 'backendAddressPool2' + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + inboundNatRules: [ + { + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 443 + idleTimeoutInMinutes: 4 + name: 'inboundNatRule1' + protocol: 'Tcp' + } + { + backendPort: 3389 + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 3389 + name: 'inboundNatRule2' + } + ] + loadBalancingRules: [ + { + backendAddressPoolName: 'backendAddressPool1' + backendPort: 80 + disableOutboundSnat: true + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 80 + idleTimeoutInMinutes: 5 + loadDistribution: 'Default' + name: 'publicIPLBRule1' + probeName: 'probe1' + protocol: 'Tcp' + } + { + backendAddressPoolName: 'backendAddressPool2' + backendPort: 8080 + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 8080 + loadDistribution: 'Default' + name: 'publicIPLBRule2' + probeName: 'probe2' + } + ] + lock: 'CanNotDelete' + outboundRules: [ + { + allocatedOutboundPorts: 63984 + backendAddressPoolName: 'backendAddressPool1' + frontendIPConfigurationName: 'publicIPConfig1' + name: 'outboundRule1' + } + ] + probes: [ + { + intervalInSeconds: 10 + name: 'probe1' + numberOfProbes: 5 + port: 80 + protocol: 'Tcp' + } + { + name: 'probe2' + port: 443 + protocol: 'Https' + requestPath: '/' + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/dependencies.bicep new file mode 100644 index 000000000..a0ce801b7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/dependencies.bicep @@ -0,0 +1,41 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@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 diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/deploy.test.bicep new file mode 100644 index 000000000..752afba44 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/internal/deploy.test.bicep @@ -0,0 +1,138 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.loadbalancers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nlbint' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + frontendIPConfigurations: [ + { + name: 'privateIPConfig1' + subnetId: nestedDependencies.outputs.subnetResourceId + } + ] + backendAddressPools: [ + { + name: 'servers' + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + inboundNatRules: [ + { + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 443 + idleTimeoutInMinutes: 4 + name: 'inboundNatRule1' + protocol: 'Tcp' + } + { + backendPort: 3389 + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 3389 + name: 'inboundNatRule2' + } + ] + skuName: 'Standard' + loadBalancingRules: [ + { + backendAddressPoolName: 'servers' + backendPort: 0 + disableOutboundSnat: true + enableFloatingIP: true + enableTcpReset: false + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 0 + idleTimeoutInMinutes: 4 + loadDistribution: 'Default' + name: 'privateIPLBRule1' + probeName: 'probe1' + protocol: 'All' + } + ] + probes: [ + { + intervalInSeconds: 5 + name: 'probe1' + numberOfProbes: 2 + port: '62000' + protocol: 'Tcp' + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/dependencies.bicep new file mode 100644 index 000000000..6f05edd3d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/dependencies.bicep @@ -0,0 +1,25 @@ +@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 + +resource publicIP 'Microsoft.Network/publicIPAddresses@2022-01-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +@description('The resource ID of the created Public IP.') +output publicIPResourceId string = publicIP.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/deploy.test.bicep new file mode 100644 index 000000000..af71851eb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/.test/min/deploy.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.loadbalancers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nlbmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + publicIPName: 'dep-<>-pip-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + + frontendIPConfigurations: [ + { + name: 'publicIPConfig1' + publicIPAddressId: nestedDependencies.outputs.publicIPResourceId + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep new file mode 100644 index 000000000..eca0b8000 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/deploy.bicep @@ -0,0 +1,52 @@ +@description('Conditional. The name of the parent load balancer. Required if the template is used in a standalone deployment.') +param loadBalancerName string + +@description('Required. The name of the backend address pool.') +param name string + +@description('Optional. An array of backend addresses.') +param loadBalancerBackendAddresses array = [] + +@description('Optional. An array of gateway load balancer tunnel interfaces.') +param tunnelInterfaces array = [] + +@description('Optional. Amount of seconds Load Balancer waits for before sending RESET to client and backend address. if value is 0 then this property will be set to null. Subscription must register the feature Microsoft.Network/SLBAllowConnectionDraining before using this property.') +param drainPeriodInSeconds int = 0 + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2021-08-01' existing = { + name: loadBalancerName +} + +resource backendAddressPool 'Microsoft.Network/loadBalancers/backendAddressPools@2022-07-01' = { + name: name + properties: { + loadBalancerBackendAddresses: loadBalancerBackendAddresses + tunnelInterfaces: tunnelInterfaces + drainPeriodInSeconds: drainPeriodInSeconds != 0 ? drainPeriodInSeconds : null + } + parent: loadBalancer +} + +@description('The name of the backend address pool.') +output name string = backendAddressPool.name + +@description('The resource ID of the backend address pool.') +output resourceId string = backendAddressPool.id + +@description('The resource group the backend address pool was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/readme.md new file mode 100644 index 000000000..5a27da64f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/readme.md @@ -0,0 +1,52 @@ +# Load Balancers Backend Address Pools `[Microsoft.Network/loadBalancers/backendAddressPools]` + +This module deploys load balancer backend address pools. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/loadBalancers/backendAddressPools` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/loadBalancers/backendAddressPools) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backend address pool. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `loadBalancerName` | string | The name of the parent load balancer. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `drainPeriodInSeconds` | int | `0` | Amount of seconds Load Balancer waits for before sending RESET to client and backend address. if value is 0 then this property will be set to null. Subscription must register the feature Microsoft.Network/SLBAllowConnectionDraining before using this property. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `loadBalancerBackendAddresses` | array | `[]` | An array of backend addresses. | +| `tunnelInterfaces` | array | `[]` | An array of gateway load balancer tunnel interfaces. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the backend address pool. | +| `resourceGroupName` | string | The resource group the backend address pool was deployed into. | +| `resourceId` | string | The resource ID of the backend address pool. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/backendAddressPools/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/deploy.bicep new file mode 100644 index 000000000..e3d25fcff --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/deploy.bicep @@ -0,0 +1,276 @@ +@description('Required. The Proximity Placement Groups Name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Name of a load balancer SKU.') +@allowed([ + 'Basic' + 'Standard' +]) +param skuName string = 'Standard' + +@description('Required. Array of objects containing all frontend IP configurations.') +@minLength(1) +param frontendIPConfigurations array + +@description('Optional. Collection of backend address pools used by a load balancer.') +param backendAddressPools array = [] + +@description('Optional. Array of objects containing all load balancing rules.') +param loadBalancingRules array = [] + +@description('Optional. Array of objects containing all probes, these are references in the load balancing rules.') +param probes array = [] + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Collection of inbound NAT Rules used by a load balancer. Defining inbound NAT rules on your load balancer is mutually exclusive with defining an inbound NAT pool. Inbound NAT pools are referenced from virtual machine scale sets. NICs that are associated with individual virtual machines cannot reference an Inbound NAT pool. They have to reference individual inbound NAT rules.') +param inboundNatRules array = [] + +@description('Optional. The outbound rules.') +param outboundRules array = [] + +var frontendIPConfigurationsVar = [for (frontendIPConfiguration, index) in frontendIPConfigurations: { + name: frontendIPConfiguration.name + properties: { + subnet: contains(frontendIPConfiguration, 'subnetId') && !empty(frontendIPConfiguration.subnetId) ? { + id: frontendIPConfiguration.subnetId + } : null + publicIPAddress: contains(frontendIPConfiguration, 'publicIPAddressId') && !empty(frontendIPConfiguration.publicIPAddressId) ? { + id: frontendIPConfiguration.publicIPAddressId + } : null + privateIPAddress: contains(frontendIPConfiguration, 'privateIPAddress') && !empty(frontendIPConfiguration.privateIPAddress) ? frontendIPConfiguration.privateIPAddress : null + privateIPAddressVersion: contains(frontendIPConfiguration, 'privateIPAddressVersion') ? frontendIPConfiguration.privateIPAddressVersion : 'IPv4' + privateIPAllocationMethod: contains(frontendIPConfiguration, 'subnetId') && !empty(frontendIPConfiguration.subnetId) ? (contains(frontendIPConfiguration, 'privateIPAddress') ? 'Static' : 'Dynamic') : null + gatewayLoadBalancer: contains(frontendIPConfiguration, 'gatewayLoadBalancer') && !empty(frontendIPConfiguration.gatewayLoadBalancer) ? { + id: frontendIPConfiguration.gatewayLoadBalancer + } : null + publicIPPrefix: contains(frontendIPConfiguration, 'publicIPPrefix') && !empty(frontendIPConfiguration.publicIPPrefix) ? { + id: frontendIPConfiguration.publicIPPrefix + } : null + } +}] + +var loadBalancingRulesVar = [for loadBalancingRule in loadBalancingRules: { + name: loadBalancingRule.name + properties: { + backendAddressPool: { + id: az.resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, loadBalancingRule.backendAddressPoolName) + } + backendPort: loadBalancingRule.backendPort + disableOutboundSnat: contains(loadBalancingRule, 'disableOutboundSnat') ? loadBalancingRule.disableOutboundSnat : true + enableFloatingIP: contains(loadBalancingRule, 'enableFloatingIP') ? loadBalancingRule.enableFloatingIP : false + enableTcpReset: contains(loadBalancingRule, 'enableTcpReset') ? loadBalancingRule.enableTcpReset : false + frontendIPConfiguration: { + id: az.resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, loadBalancingRule.frontendIPConfigurationName) + } + frontendPort: loadBalancingRule.frontendPort + idleTimeoutInMinutes: contains(loadBalancingRule, 'idleTimeoutInMinutes') ? loadBalancingRule.idleTimeoutInMinutes : 4 + loadDistribution: contains(loadBalancingRule, 'loadDistribution') ? loadBalancingRule.loadDistribution : 'Default' + probe: { + id: '${az.resourceId('Microsoft.Network/loadBalancers', name)}/probes/${loadBalancingRule.probeName}' + } + protocol: contains(loadBalancingRule, 'protocol') ? loadBalancingRule.protocol : 'Tcp' + } +}] + +var outboundRulesVar = [for outboundRule in outboundRules: { + name: outboundRule.name + properties: { + frontendIPConfigurations: [ + { + id: az.resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', name, outboundRule.frontendIPConfigurationName) + } + ] + backendAddressPool: { + id: az.resourceId('Microsoft.Network/loadBalancers/backendAddressPools', name, outboundRule.backendAddressPoolName) + } + protocol: contains(outboundRule, 'protocol') ? outboundRule.protocol : 'All' + allocatedOutboundPorts: contains(outboundRule, 'allocatedOutboundPorts') ? outboundRule.allocatedOutboundPorts : 63984 + enableTcpReset: contains(outboundRule, 'enableTcpReset') ? outboundRule.enableTcpReset : true + idleTimeoutInMinutes: contains(outboundRule, 'idleTimeoutInMinutes') ? outboundRule.idleTimeoutInMinutes : 4 + } +}] + +var probesVar = [for probe in probes: { + name: probe.name + properties: { + protocol: contains(probe, 'protocol') ? probe.protocol : 'Tcp' + requestPath: toLower(probe.protocol) != 'tcp' ? probe.requestPath : null + port: contains(probe, 'port') ? probe.port : 80 + intervalInSeconds: contains(probe, 'intervalInSeconds') ? probe.intervalInSeconds : 5 + numberOfProbes: contains(probe, 'numberOfProbes') ? probe.numberOfProbes : 2 + } +}] + +var backendAddressPoolNames = [for backendAddressPool in backendAddressPools: { + name: backendAddressPool.name +}] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var enableReferencedModulesTelemetry = false + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { + name: name + location: location + tags: tags + sku: { + name: skuName + } + properties: { + frontendIPConfigurations: frontendIPConfigurationsVar + loadBalancingRules: loadBalancingRulesVar + backendAddressPools: backendAddressPoolNames + outboundRules: outboundRulesVar + probes: probesVar + } +} + +module loadBalancer_backendAddressPools 'backendAddressPools/deploy.bicep' = [for (backendAddressPool, index) in backendAddressPools: { + name: '${uniqueString(deployment().name, location)}-loadBalancer-backendAddressPools-${index}' + params: { + loadBalancerName: loadBalancer.name + name: backendAddressPool.name + tunnelInterfaces: contains(backendAddressPool, 'tunnelInterfaces') && !empty(backendAddressPool.tunnelInterfaces) ? backendAddressPool.tunnelInterfaces : [] + loadBalancerBackendAddresses: contains(backendAddressPool, 'loadBalancerBackendAddresses') && !empty(backendAddressPool.loadBalancerBackendAddresses) ? backendAddressPool.loadBalancerBackendAddresses : [] + drainPeriodInSeconds: contains(backendAddressPool, 'drainPeriodInSeconds') ? backendAddressPool.drainPeriodInSeconds : 0 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module loadBalancer_inboundNATRules 'inboundNatRules/deploy.bicep' = [for (inboundNATRule, index) in inboundNatRules: { + name: '${uniqueString(deployment().name, location)}-LoadBalancer-inboundNatRules-${index}' + params: { + loadBalancerName: loadBalancer.name + name: inboundNATRule.name + frontendIPConfigurationName: inboundNATRule.frontendIPConfigurationName + frontendPort: inboundNATRule.frontendPort + backendPort: contains(inboundNATRule, 'backendPort') ? inboundNATRule.backendPort : inboundNATRule.frontendPort + backendAddressPoolName: contains(inboundNATRule, 'backendAddressPoolName') ? inboundNATRule.backendAddressPoolName : '' + enableFloatingIP: contains(inboundNATRule, 'enableFloatingIP') ? inboundNATRule.enableFloatingIP : false + enableTcpReset: contains(inboundNATRule, 'enableTcpReset') ? inboundNATRule.enableTcpReset : false + frontendPortRangeEnd: contains(inboundNATRule, 'frontendPortRangeEnd') ? inboundNATRule.frontendPortRangeEnd : -1 + frontendPortRangeStart: contains(inboundNATRule, 'frontendPortRangeStart') ? inboundNATRule.frontendPortRangeStart : -1 + idleTimeoutInMinutes: contains(inboundNATRule, 'idleTimeoutInMinutes') ? inboundNATRule.idleTimeoutInMinutes : 4 + protocol: contains(inboundNATRule, 'protocol') ? inboundNATRule.protocol : 'Tcp' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + loadBalancer_backendAddressPools + ] +}] + +resource loadBalancer_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${loadBalancer.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: loadBalancer +} + +resource loadBalancer_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + } + scope: loadBalancer +} + +module loadBalancer_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LoadBalancer-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: loadBalancer.id + } +}] + +@description('The name of the load balancer.') +output name string = loadBalancer.name + +@description('The resource ID of the load balancer.') +output resourceId string = loadBalancer.id + +@description('The resource group the load balancer was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The backend address pools available in the load balancer.') +output backendpools array = loadBalancer.properties.backendAddressPools + +@description('The location the resource was deployed into.') +output location string = loadBalancer.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep new file mode 100644 index 000000000..b0c533ff8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/deploy.bicep @@ -0,0 +1,97 @@ +@description('Conditional. The name of the parent load balancer. Required if the template is used in a standalone deployment.') +param loadBalancerName string + +@description('Required. The name of the inbound NAT rule.') +param name string + +@description('Required. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer.') +@minValue(1) +@maxValue(65534) +param frontendPort int + +@description('Optional. The port used for the internal endpoint.') +@minValue(1) +@maxValue(65535) +param backendPort int = frontendPort + +@description('Optional. Name of the backend address pool.') +param backendAddressPoolName string = '' + +@description('Optional. Configures a virtual machine\'s endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can\'t be changed after you create the endpoint.') +param enableFloatingIP bool = false + +@description('Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP.') +param enableTcpReset bool = false + +@description('Required. The name of the frontend IP address to set for the inbound NAT rule.') +param frontendIPConfigurationName string + +@description('Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool.') +@minValue(-1) +@maxValue(65534) +param frontendPortRangeEnd int = -1 + +@description('Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool.') +@minValue(-1) +@maxValue(65534) +param frontendPortRangeStart int = -1 + +@description('Optional. The timeout for the TCP idle connection. The value can be set between 4 and 30 minutes. The default value is 4 minutes. This element is only used when the protocol is set to TCP.') +param idleTimeoutInMinutes int = 4 + +@description('Optional. The transport protocol for the endpoint.') +@allowed([ + 'All' + 'Tcp' + 'Udp' +]) +param protocol string = 'Tcp' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' existing = { + name: loadBalancerName +} + +resource inboundNatRule 'Microsoft.Network/loadBalancers/inboundNatRules@2022-07-01' = { + name: name + properties: { + frontendPort: frontendPort + backendPort: backendPort + backendAddressPool: !empty(backendAddressPoolName) ? { + id: '${loadBalancer.id}/backendAddressPools/${backendAddressPoolName}' + } : null + enableFloatingIP: enableFloatingIP + enableTcpReset: enableTcpReset + frontendIPConfiguration: { + id: '${loadBalancer.id}/frontendIPConfigurations/${frontendIPConfigurationName}' + } + frontendPortRangeStart: frontendPortRangeStart != -1 ? frontendPortRangeStart : null + frontendPortRangeEnd: frontendPortRangeEnd != -1 ? frontendPortRangeEnd : null + idleTimeoutInMinutes: idleTimeoutInMinutes + protocol: protocol + } + parent: loadBalancer +} + +@description('The name of the inbound NAT rule.') +output name string = inboundNatRule.name + +@description('The resource ID of the inbound NAT rule.') +output resourceId string = inboundNatRule.id + +@description('The resource group the inbound NAT rule was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/readme.md new file mode 100644 index 000000000..5a5fbc1c6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/readme.md @@ -0,0 +1,59 @@ +# Load Balancer Inbound NAT Rules `[Microsoft.Network/loadBalancers/inboundNatRules]` + +This module deploys load balancers inbound NAT rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/loadBalancers/inboundNatRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/loadBalancers/inboundNatRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `frontendIPConfigurationName` | string | The name of the frontend IP address to set for the inbound NAT rule. | +| `frontendPort` | int | The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. | +| `name` | string | The name of the inbound NAT rule. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `loadBalancerName` | string | The name of the parent load balancer. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backendAddressPoolName` | string | `''` | | Name of the backend address pool. | +| `backendPort` | int | `[parameters('frontendPort')]` | | The port used for the internal endpoint. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableFloatingIP` | bool | `False` | | Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint. | +| `enableTcpReset` | bool | `False` | | Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP. | +| `frontendPortRangeEnd` | int | `-1` | | The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. | +| `frontendPortRangeStart` | int | `-1` | | The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. | +| `idleTimeoutInMinutes` | int | `4` | | The timeout for the TCP idle connection. The value can be set between 4 and 30 minutes. The default value is 4 minutes. This element is only used when the protocol is set to TCP. | +| `protocol` | string | `'Tcp'` | `[All, Tcp, Udp]` | The transport protocol for the endpoint. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the inbound NAT rule. | +| `resourceGroupName` | string | The resource group the inbound NAT rule was deployed into. | +| `resourceId` | string | The resource ID of the inbound NAT rule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/inboundNatRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/readme.md new file mode 100644 index 000000000..49611acb6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/readme.md @@ -0,0 +1,1019 @@ +# Load Balancers `[Microsoft.Network/loadBalancers]` + +This module deploys a load balancer. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/loadBalancers` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/loadBalancers) | +| `Microsoft.Network/loadBalancers/backendAddressPools` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/loadBalancers/backendAddressPools) | +| `Microsoft.Network/loadBalancers/inboundNatRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/loadBalancers/inboundNatRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `frontendIPConfigurations` | array | Array of objects containing all frontend IP configurations. | +| `name` | string | The Proximity Placement Groups Name. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `backendAddressPools` | _[backendAddressPools](backendAddressPools/readme.md)_ array | `[]` | | Collection of backend address pools used by a load balancer. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `inboundNatRules` | _[inboundNatRules](inboundNatRules/readme.md)_ array | `[]` | | Collection of inbound NAT Rules used by a load balancer. Defining inbound NAT rules on your load balancer is mutually exclusive with defining an inbound NAT pool. Inbound NAT pools are referenced from virtual machine scale sets. NICs that are associated with individual virtual machines cannot reference an Inbound NAT pool. They have to reference individual inbound NAT rules. | +| `loadBalancingRules` | array | `[]` | | Array of objects containing all load balancing rules. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `outboundRules` | array | `[]` | | The outbound rules. | +| `probes` | array | `[]` | | Array of objects containing all probes, these are references in the load balancing rules. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `skuName` | string | `'Standard'` | `[Basic, Standard]` | Name of a load balancer SKU. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `frontendIPConfigurations` + +

+ +Parameter JSON format + +```json +"frontendIPConfigurations": { + "value": [ + { + "name": "p_hub-bfw-server-feip", + "properties": { + "publicIPAddressId": "[reference(variables('deploymentPIP-VPN')).outputs.publicIPAddressResourceId.value]", + "subnetId": "", + "privateIPAddress": "" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +frontendIPConfigurations: [ + { + name: 'p_hub-bfw-server-feip' + properties: { + publicIPAddressId: '[reference(variables('deploymentPIP-VPN')).outputs.publicIPAddressResourceId.value]' + subnetId: '' + privateIPAddress: '' + } + } +] +``` + +
+

+ +### Parameter Usage: `backendAddressPools` + +

+ +Parameter JSON format + +```json +"backendAddressPools": { + "value": [ + { + "name": "p_hub-bfw-server-bepool", + "properties": { + "loadBalancerBackendAddresses": [ + { + "name": "iacs-sh-main-pd-01-euw-rg-network_awefwa01p-nic-int-01ipconfig-internal", + "properties": { + "virtualNetwork": { + "id": "[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]" + }, + "ipAddress": "172.22.232.5" + } + }, + { + "name": "iacs-sh-main-pd-01-euw-rg-network_awefwa01p-ha-nic-int-01ipconfig-internal", + "properties": { + "virtualNetwork": { + "id": "[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]" + }, + "ipAddress": "172.22.232.6" + } + } + ] + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +backendAddressPools: [ + { + name: 'p_hub-bfw-server-bepool' + properties: { + loadBalancerBackendAddresses: [ + { + name: 'iacs-sh-main-pd-01-euw-rg-network_awefwa01p-nic-int-01ipconfig-internal' + properties: { + virtualNetwork: { + id: '[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]' + } + ipAddress: '172.22.232.5' + } + } + { + name: 'iacs-sh-main-pd-01-euw-rg-network_awefwa01p-ha-nic-int-01ipconfig-internal' + properties: { + virtualNetwork: { + id: '[reference(variables('deploymentVNET')).outputs.vNetResourceId.value]' + } + ipAddress: '172.22.232.6' + } + } + ] + } + } +] +``` + +
+

+ +### Parameter Usage: `loadBalancingRules` + +

+ +Parameter JSON format + +```json +"loadBalancingRules": { + "value": [ + { + "name": "p_hub-bfw-server-IPSEC-IKE-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Udp", + "frontendPort": 500, + "backendPort": 500, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + }, + { + "name": "p_hub-bfw-server-IPSEC-NATT-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Udp", + "frontendPort": 4500, + "backendPort": 4500, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + }, + { + "name": "p_hub-bfw-server-TINA-UDP-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Udp", + "frontendPort": 691, + "backendPort": 691, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + }, + { + "name": "p_hub-bfw-server-TINA-TCP-lbrule", + "properties": { + "frontendIPConfigurationName": "p_hub-bfw-server-feip", + "backendAddressPoolName": "p_hub-bfw-server-bepool", + "protocol": "Tcp", + "frontendPort": 691, + "backendPort": 691, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 5, + "probeName": "p_hub-bfw-server-tcp-65001-probe" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +loadBalancingRules: [ + { + name: 'p_hub-bfw-server-IPSEC-IKE-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Udp' + frontendPort: 500 + backendPort: 500 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } + { + name: 'p_hub-bfw-server-IPSEC-NATT-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Udp' + frontendPort: 4500 + backendPort: 4500 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } + { + name: 'p_hub-bfw-server-TINA-UDP-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Udp' + frontendPort: 691 + backendPort: 691 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } + { + name: 'p_hub-bfw-server-TINA-TCP-lbrule' + properties: { + frontendIPConfigurationName: 'p_hub-bfw-server-feip' + backendAddressPoolName: 'p_hub-bfw-server-bepool' + protocol: 'Tcp' + frontendPort: 691 + backendPort: 691 + enableFloatingIP: false + idleTimeoutInMinutes: 5 + probeName: 'p_hub-bfw-server-tcp-65001-probe' + } + } +] +``` + +
+

+ +### Parameter Usage: `probes` + +

+ +Parameter JSON format + +```json +"probes": { + "value": [ + { + "name": "p_hub-bfw-server-tcp-65001-probe", + "properties": { + "protocol": "Tcp", + "port": 65001, + "intervalInSeconds": 5, + "numberOfProbes": 2 + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +probes: [ + { + name: 'p_hub-bfw-server-tcp-65001-probe' + properties: { + protocol: 'Tcp' + port: 65001 + intervalInSeconds: 5 + numberOfProbes: 2 + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `backendpools` | array | The backend address pools available in the load balancer. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the load balancer. | +| `resourceGroupName` | string | The resource group the load balancer was deployed into. | +| `resourceId` | string | The resource ID of the load balancer. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module loadBalancers './Microsoft.Network/loadBalancers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nlbcom' + params: { + // Required parameters + frontendIPConfigurations: [ + { + name: 'publicIPConfig1' + publicIPAddressId: '' + } + ] + name: '<>nlbcom001' + // Non-required parameters + backendAddressPools: [ + { + name: 'backendAddressPool1' + } + { + name: 'backendAddressPool2' + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + inboundNatRules: [ + { + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 443 + idleTimeoutInMinutes: 4 + name: 'inboundNatRule1' + protocol: 'Tcp' + } + { + backendPort: 3389 + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 3389 + name: 'inboundNatRule2' + } + ] + loadBalancingRules: [ + { + backendAddressPoolName: 'backendAddressPool1' + backendPort: 80 + disableOutboundSnat: true + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 80 + idleTimeoutInMinutes: 5 + loadDistribution: 'Default' + name: 'publicIPLBRule1' + probeName: 'probe1' + protocol: 'Tcp' + } + { + backendAddressPoolName: 'backendAddressPool2' + backendPort: 8080 + frontendIPConfigurationName: 'publicIPConfig1' + frontendPort: 8080 + loadDistribution: 'Default' + name: 'publicIPLBRule2' + probeName: 'probe2' + } + ] + lock: 'CanNotDelete' + outboundRules: [ + { + allocatedOutboundPorts: 63984 + backendAddressPoolName: 'backendAddressPool1' + frontendIPConfigurationName: 'publicIPConfig1' + name: 'outboundRule1' + } + ] + probes: [ + { + intervalInSeconds: 10 + name: 'probe1' + numberOfProbes: 5 + port: 80 + protocol: 'Tcp' + } + { + name: 'probe2' + port: 443 + protocol: 'Https' + requestPath: '/' + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "frontendIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "publicIPAddressId": "" + } + ] + }, + "name": { + "value": "<>nlbcom001" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "backendAddressPool1" + }, + { + "name": "backendAddressPool2" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "inboundNatRules": { + "value": [ + { + "backendPort": 443, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 443, + "idleTimeoutInMinutes": 4, + "name": "inboundNatRule1", + "protocol": "Tcp" + }, + { + "backendPort": 3389, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 3389, + "name": "inboundNatRule2" + } + ] + }, + "loadBalancingRules": { + "value": [ + { + "backendAddressPoolName": "backendAddressPool1", + "backendPort": 80, + "disableOutboundSnat": true, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 80, + "idleTimeoutInMinutes": 5, + "loadDistribution": "Default", + "name": "publicIPLBRule1", + "probeName": "probe1", + "protocol": "Tcp" + }, + { + "backendAddressPoolName": "backendAddressPool2", + "backendPort": 8080, + "frontendIPConfigurationName": "publicIPConfig1", + "frontendPort": 8080, + "loadDistribution": "Default", + "name": "publicIPLBRule2", + "probeName": "probe2" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "outboundRules": { + "value": [ + { + "allocatedOutboundPorts": 63984, + "backendAddressPoolName": "backendAddressPool1", + "frontendIPConfigurationName": "publicIPConfig1", + "name": "outboundRule1" + } + ] + }, + "probes": { + "value": [ + { + "intervalInSeconds": 10, + "name": "probe1", + "numberOfProbes": 5, + "port": 80, + "protocol": "Tcp" + }, + { + "name": "probe2", + "port": 443, + "protocol": "Https", + "requestPath": "/" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Internal

+ +
+ +via Bicep module + +```bicep +module loadBalancers './Microsoft.Network/loadBalancers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nlbint' + params: { + // Required parameters + frontendIPConfigurations: [ + { + name: 'privateIPConfig1' + subnetId: '' + } + ] + name: '<>nlbint001' + // Non-required parameters + backendAddressPools: [ + { + name: 'servers' + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + inboundNatRules: [ + { + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 443 + idleTimeoutInMinutes: 4 + name: 'inboundNatRule1' + protocol: 'Tcp' + } + { + backendPort: 3389 + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 3389 + name: 'inboundNatRule2' + } + ] + loadBalancingRules: [ + { + backendAddressPoolName: 'servers' + backendPort: 0 + disableOutboundSnat: true + enableFloatingIP: true + enableTcpReset: false + frontendIPConfigurationName: 'privateIPConfig1' + frontendPort: 0 + idleTimeoutInMinutes: 4 + loadDistribution: 'Default' + name: 'privateIPLBRule1' + probeName: 'probe1' + protocol: 'All' + } + ] + probes: [ + { + intervalInSeconds: 5 + name: 'probe1' + numberOfProbes: 2 + port: '62000' + protocol: 'Tcp' + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + tags: { + Environment: 'Non-Prod' + 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 + "frontendIPConfigurations": { + "value": [ + { + "name": "privateIPConfig1", + "subnetId": "" + } + ] + }, + "name": { + "value": "<>nlbint001" + }, + // Non-required parameters + "backendAddressPools": { + "value": [ + { + "name": "servers" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "inboundNatRules": { + "value": [ + { + "backendPort": 443, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 443, + "idleTimeoutInMinutes": 4, + "name": "inboundNatRule1", + "protocol": "Tcp" + }, + { + "backendPort": 3389, + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 3389, + "name": "inboundNatRule2" + } + ] + }, + "loadBalancingRules": { + "value": [ + { + "backendAddressPoolName": "servers", + "backendPort": 0, + "disableOutboundSnat": true, + "enableFloatingIP": true, + "enableTcpReset": false, + "frontendIPConfigurationName": "privateIPConfig1", + "frontendPort": 0, + "idleTimeoutInMinutes": 4, + "loadDistribution": "Default", + "name": "privateIPLBRule1", + "probeName": "probe1", + "protocol": "All" + } + ] + }, + "probes": { + "value": [ + { + "intervalInSeconds": 5, + "name": "probe1", + "numberOfProbes": 2, + "port": "62000", + "protocol": "Tcp" + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuName": { + "value": "Standard" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module loadBalancers './Microsoft.Network/loadBalancers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nlbmin' + params: { + // Required parameters + frontendIPConfigurations: [ + { + name: 'publicIPConfig1' + publicIPAddressId: '' + } + ] + name: '<>nlbmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "frontendIPConfigurations": { + "value": [ + { + "name": "publicIPConfig1", + "publicIPAddressId": "" + } + ] + }, + "name": { + "value": "<>nlbmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/loadBalancers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..e6ba4dc72 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(localNetworkGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: localNetworkGateway +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/deploy.test.bicep new file mode 100644 index 000000000..3268ea306 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/common/deploy.test.bicep @@ -0,0 +1,70 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.localnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nlngcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + localAddressPrefixes: [ + '192.168.1.0/24' + ] + localGatewayPublicIpAddress: '8.8.8.8' + localAsn: '65123' + localBgpPeeringAddress: '192.168.1.5' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/min/deploy.test.bicep new file mode 100644 index 000000000..db5d7789d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/.test/min/deploy.test.bicep @@ -0,0 +1,46 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.localnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nlngmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + localAddressPrefixes: [ + '192.168.1.0/24' + ] + localGatewayPublicIpAddress: '8.8.8.8' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/deploy.bicep new file mode 100644 index 000000000..bc1d4e387 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/deploy.bicep @@ -0,0 +1,107 @@ +@description('Required. Name of the Local Network Gateway.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. List of the local (on-premises) IP address ranges.') +param localAddressPrefixes array + +@description('Required. Public IP of the local gateway.') +param localGatewayPublicIpAddress string + +@description('Optional. The BGP speaker\'s ASN. Not providing this value will automatically disable BGP on this Local Network Gateway resource.') +param localAsn string = '' + +@description('Optional. The BGP peering address and BGP identifier of this BGP speaker. Not providing this value will automatically disable BGP on this Local Network Gateway resource.') +param localBgpPeeringAddress string = '' + +@description('Optional. The weight added to routes learned from this BGP speaker. This will only take effect if both the localAsn and the localBgpPeeringAddress values are provided.') +param localPeerWeight string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. FQDN of local network gateway.') +param fqdn string = '' + +var bgpSettings = { + asn: localAsn + bgpPeeringAddress: localBgpPeeringAddress + peerWeight: !empty(localPeerWeight) ? localPeerWeight : '0' +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + localNetworkAddressSpace: { + addressPrefixes: localAddressPrefixes + } + fqdn: !empty(fqdn) ? fqdn : null + gatewayIpAddress: localGatewayPublicIpAddress + bgpSettings: !empty(localAsn) && !empty(localBgpPeeringAddress) ? bgpSettings : null + } +} + +resource localNetworkGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${localNetworkGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: localNetworkGateway +} + +module localNetworkGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LocalNetworkGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: localNetworkGateway.id + } +}] + +@description('The resource ID of the local network gateway.') +output resourceId string = localNetworkGateway.id + +@description('The resource group the local network gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the local network gateway.') +output name string = localNetworkGateway.name + +@description('The location the resource was deployed into.') +output location string = localNetworkGateway.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/readme.md new file mode 100644 index 000000000..39a6ba8ae --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/readme.md @@ -0,0 +1,320 @@ +# Local Network Gateways `[Microsoft.Network/localNetworkGateways]` + +This module deploys a local network gateway. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/localNetworkGateways` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/localNetworkGateways) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `localAddressPrefixes` | array | List of the local (on-premises) IP address ranges. | +| `localGatewayPublicIpAddress` | string | Public IP of the local gateway. | +| `name` | string | Name of the Local Network Gateway. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `fqdn` | string | `''` | | FQDN of local network gateway. | +| `localAsn` | string | `''` | | The BGP speaker's ASN. Not providing this value will automatically disable BGP on this Local Network Gateway resource. | +| `localBgpPeeringAddress` | string | `''` | | The BGP peering address and BGP identifier of this BGP speaker. Not providing this value will automatically disable BGP on this Local Network Gateway resource. | +| `localPeerWeight` | string | `''` | | The weight added to routes learned from this BGP speaker. This will only take effect if both the localAsn and the localBgpPeeringAddress values are provided. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the local network gateway. | +| `resourceGroupName` | string | The resource group the local network gateway was deployed into. | +| `resourceId` | string | The resource ID of the local network gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module localNetworkGateways './Microsoft.Network/localNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nlngcom' + params: { + // Required parameters + localAddressPrefixes: [ + '192.168.1.0/24' + ] + localGatewayPublicIpAddress: '8.8.8.8' + name: '<>nlngcom001' + // Non-required parameters + enableDefaultTelemetry: '' + localAsn: '65123' + localBgpPeeringAddress: '192.168.1.5' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "localAddressPrefixes": { + "value": [ + "192.168.1.0/24" + ] + }, + "localGatewayPublicIpAddress": { + "value": "8.8.8.8" + }, + "name": { + "value": "<>nlngcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "localAsn": { + "value": "65123" + }, + "localBgpPeeringAddress": { + "value": "192.168.1.5" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module localNetworkGateways './Microsoft.Network/localNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nlngmin' + params: { + // Required parameters + localAddressPrefixes: [ + '192.168.1.0/24' + ] + localGatewayPublicIpAddress: '8.8.8.8' + name: '<>nlngmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "localAddressPrefixes": { + "value": [ + "192.168.1.0/24" + ] + }, + "localGatewayPublicIpAddress": { + "value": "8.8.8.8" + }, + "name": { + "value": "<>nlngmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/localNetworkGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..7136a083f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource natGateway 'Microsoft.Network/natGateways@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(natGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: natGateway +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/deploy.test.bicep new file mode 100644 index 000000000..9724506a6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/.test/common/deploy.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.natgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nngcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + natGatewayPublicIpAddress: true + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/deploy.bicep new file mode 100644 index 000000000..bd53b35a6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/deploy.bicep @@ -0,0 +1,183 @@ +@description('Required. Name of the Azure Bastion resource.') +param name string + +@description('Optional. The idle timeout of the NAT gateway.') +param idleTimeoutInMinutes int = 5 + +@description('Optional. Use to have a new Public IP Address created for the NAT Gateway.') +param natGatewayPublicIpAddress bool = false + +@description('Optional. Specifies the name of the Public IP used by the NAT Gateway. If it\'s not provided, a \'-pip\' suffix will be appended to the Bastion\'s name.') +param natGatewayPipName string = '' + +@description('Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix.') +param publicIPPrefixResourceId string = '' + +@description('Optional. DNS name of the Public IP resource. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com.') +param domainNameLabel string = '' + +@description('Optional. Existing Public IP Address resource names to use for the NAT Gateway.') +param publicIpAddresses array = [] + +@description('Optional. Existing Public IP Prefixes resource names to use for the NAT Gateway.') +param publicIpPrefixes array = [] + +@description('Optional. A list of availability zones denoting the zone in which Nat Gateway should be deployed.') +param zones array = [] + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags for the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the public IP diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var publicIPPrefixResourceIds = [for publicIpPrefix in publicIpPrefixes: { + id: az.resourceId('Microsoft.Network/publicIPPrefixes', publicIpPrefix) +}] + +var publicIPAddressResourceIds = [for publicIpAddress in publicIpAddresses: { + id: az.resourceId('Microsoft.Network/publicIPAddresses', publicIpAddress) +}] + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// PUBLIC IP +// ========= +module publicIPAddress '../publicIPAddresses/deploy.bicep' = if (natGatewayPublicIpAddress) { + name: '${uniqueString(deployment().name, location)}-NatGateway-PIP' + params: { + name: !empty(natGatewayPipName) ? natGatewayPipName : '${name}-pip' + diagnosticLogCategoriesToEnable: diagnosticLogCategoriesToEnable + diagnosticMetricsToEnable: diagnosticMetricsToEnable + diagnosticSettingsName: !empty(diagnosticSettingsName) ? diagnosticSettingsName : (!empty(natGatewayPipName) ? '${natGatewayPipName}-diagnosticSettings' : '${name}-pip-diagnosticSettings') + diagnosticLogsRetentionInDays: diagnosticLogsRetentionInDays + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + domainNameLabel: domainNameLabel + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: location + lock: lock + publicIPAllocationMethod: 'Static' + publicIPPrefixResourceId: publicIPPrefixResourceId + tags: tags + skuName: 'Standard' + } +} + +// NAT GATEWAY +// =========== +resource natGateway 'Microsoft.Network/natGateways@2022-07-01' = { + name: name + location: location + tags: tags + sku: { + name: 'Standard' + } + properties: { + idleTimeoutInMinutes: idleTimeoutInMinutes + publicIpPrefixes: publicIPPrefixResourceIds + publicIpAddresses: publicIPAddressResourceIds + } + zones: zones +} + +resource natGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${natGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: natGateway +} + +module natGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NatGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: natGateway.id + } +}] + +@description('The name of the NAT Gateway.') +output name string = natGateway.name + +@description('The resource ID of the NAT Gateway.') +output resourceId string = natGateway.id + +@description('The resource group the NAT Gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = natGateway.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/readme.md new file mode 100644 index 000000000..3a332b39b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/readme.md @@ -0,0 +1,283 @@ +# NAT Gateways `[Microsoft.Network/natGateways]` + +This module deploys a NAT gateway. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/natGateways` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/natGateways) | +| `Microsoft.Network/publicIPAddresses` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/publicIPAddresses) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Azure Bastion resource. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the public IP diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `domainNameLabel` | string | `''` | | DNS name of the Public IP resource. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `idleTimeoutInMinutes` | int | `5` | | The idle timeout of the NAT gateway. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natGatewayPipName` | string | `''` | | Specifies the name of the Public IP used by the NAT Gateway. If it's not provided, a '-pip' suffix will be appended to the Bastion's name. | +| `natGatewayPublicIpAddress` | bool | `False` | | Use to have a new Public IP Address created for the NAT Gateway. | +| `publicIpAddresses` | array | `[]` | | Existing Public IP Address resource names to use for the NAT Gateway. | +| `publicIpPrefixes` | array | `[]` | | Existing Public IP Prefixes resource names to use for the NAT Gateway. | +| `publicIPPrefixResourceId` | string | `''` | | Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags for the resource. | +| `zones` | array | `[]` | | A list of availability zones denoting the zone in which Nat Gateway should be deployed. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the NAT Gateway. | +| `resourceGroupName` | string | The resource group the NAT Gateway was deployed into. | +| `resourceId` | string | The resource ID of the NAT Gateway. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Network/publicIPAddresses` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module natGateways './Microsoft.Network/natGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nngcom' + params: { + // Required parameters + name: '<>nngcom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + natGatewayPublicIpAddress: true + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nngcom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "natGatewayPublicIpAddress": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/natGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..15867ee2d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkInterface.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkInterface +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/dependencies.bicep new file mode 100644 index 000000000..f0b711e43 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/dependencies.bicep @@ -0,0 +1,113 @@ +@description('Optional. The location to deploy resources 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 Application Security Group to create.') +param applicationSecurityGroupName string + +@description('Required. The name of the Load Balancer Backend Address Pool to create.') +param loadBalancerName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2022-01-01' = { + name: applicationSecurityGroupName + location: location +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2022-01-01' = { + name: loadBalancerName + location: location + sku: { + name: 'Standard' + } + + properties: { + frontendIPConfigurations: [ + { + name: 'privateIPConfig1' + properties: { + subnet: { + id: virtualNetwork.properties.subnets[0].id + } + } + } + ] + } + + resource backendPool 'backendAddressPools@2022-01-01' = { + name: 'default' + } +} + +resource inboundNatRule 'Microsoft.Network/loadBalancers/inboundNatRules@2021-08-01' = { + name: 'inboundNatRule1' + properties: { + frontendPort: 443 + backendPort: 443 + enableFloatingIP: false + enableTcpReset: false + frontendIPConfiguration: { + id: loadBalancer.properties.frontendIPConfigurations[0].id + } + idleTimeoutInMinutes: 4 + protocol: 'Tcp' + } + parent: loadBalancer +} + +resource inboundNatRule2 'Microsoft.Network/loadBalancers/inboundNatRules@2021-08-01' = { + name: 'inboundNatRule2' + properties: { + frontendPort: 3389 + backendPort: 3389 + frontendIPConfiguration: { + id: loadBalancer.properties.frontendIPConfigurations[0].id + } + idleTimeoutInMinutes: 4 + protocol: 'Tcp' + } + parent: loadBalancer +} + +@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 Application Security Group.') +output applicationSecurityGroupResourceId string = applicationSecurityGroup.id + +@description('The resource ID of the created Load Balancer Backend Pool Name.') +output loadBalancerBackendPoolResourceId string = loadBalancer::backendPool.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/deploy.test.bicep new file mode 100644 index 000000000..177fecb61 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/common/deploy.test.bicep @@ -0,0 +1,110 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.networkinterfaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nnicom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + applicationSecurityGroupName: 'dep-<>-asg-${serviceShort}' + loadBalancerName: 'dep-<>-lb-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + ipConfigurations: [ + { + applicationSecurityGroups: [ + { + id: nestedDependencies.outputs.applicationSecurityGroupResourceId + } + ] + loadBalancerBackendAddressPools: [ + { + id: nestedDependencies.outputs.loadBalancerBackendPoolResourceId + } + ] + name: 'ipconfig01' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + applicationSecurityGroups: [ + { + id: nestedDependencies.outputs.applicationSecurityGroupResourceId + } + ] + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/dependencies.bicep new file mode 100644 index 000000000..978d8fa79 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/dependencies.bicep @@ -0,0 +1,30 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/deploy.test.bicep new file mode 100644 index 000000000..d78c098c8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/.test/min/deploy.test.bicep @@ -0,0 +1,56 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.networkinterfaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nnimin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/deploy.bicep new file mode 100644 index 000000000..96bfb3fb3 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/deploy.bicep @@ -0,0 +1,183 @@ +@description('Required. The name of the network interface.') +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. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Indicates whether IP forwarding is enabled on this network interface.') +param enableIPForwarding bool = false + +@description('Optional. If the network interface is accelerated networking enabled.') +param enableAcceleratedNetworking bool = false + +@description('Optional. List of DNS servers IP addresses. Use \'AzureProvidedDNS\' to switch to azure provided DNS resolution. \'AzureProvidedDNS\' value cannot be combined with other IPs, it must be the only value in dnsServers collection.') +param dnsServers array = [] + +@description('Optional. The network security group (NSG) to attach to the network interface.') +param networkSecurityGroupResourceId string = '' + +@allowed([ + 'Floating' + 'MaxConnections' + 'None' +]) +@description('Optional. Auxiliary mode of Network Interface resource. Not all regions are enabled for Auxiliary Mode Nic.') +param auxiliaryMode string = 'None' + +@description('Optional. Indicates whether to disable tcp state tracking. Subscription must be registered for the Microsoft.Network/AllowDisableTcpStateTracking feature before this property can be set to true.') +param disableTcpStateTracking bool = false + +@description('Required. A list of IPConfigurations of the network interface.') +param ipConfigurations array + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +// @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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource identifier of log analytics.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + auxiliaryMode: auxiliaryMode + disableTcpStateTracking: disableTcpStateTracking + dnsSettings: !empty(dnsServers) ? { + dnsServers: dnsServers + } : null + enableAcceleratedNetworking: enableAcceleratedNetworking + enableIPForwarding: enableIPForwarding + networkSecurityGroup: !empty(networkSecurityGroupResourceId) ? { + id: networkSecurityGroupResourceId + } : null + ipConfigurations: [for (ipConfiguration, index) in ipConfigurations: { + name: contains(ipConfiguration, 'name') ? ipConfiguration.name : 'ipconfig0${index + 1}' + properties: { + primary: index == 0 ? true : false + privateIPAllocationMethod: contains(ipConfiguration, 'privateIPAllocationMethod') ? (!empty(ipConfiguration.privateIPAllocationMethod) ? ipConfiguration.privateIPAllocationMethod : null) : null + privateIPAddress: contains(ipConfiguration, 'privateIPAddress') ? (!empty(ipConfiguration.privateIPAddress) ? ipConfiguration.privateIPAddress : null) : null + publicIPAddress: contains(ipConfiguration, 'publicIPAddressResourceId') ? (ipConfiguration.publicIPAddressResourceId != null ? { + id: ipConfiguration.publicIPAddressResourceId + } : null) : null + subnet: { + id: ipConfiguration.subnetResourceId + } + loadBalancerBackendAddressPools: contains(ipConfiguration, 'loadBalancerBackendAddressPools') ? ipConfiguration.loadBalancerBackendAddressPools : null + applicationSecurityGroups: contains(ipConfiguration, 'applicationSecurityGroups') ? ipConfiguration.applicationSecurityGroups : null + applicationGatewayBackendAddressPools: contains(ipConfiguration, 'applicationGatewayBackendAddressPools') ? ipConfiguration.applicationGatewayBackendAddressPools : null + gatewayLoadBalancer: contains(ipConfiguration, 'gatewayLoadBalancer') ? ipConfiguration.gatewayLoadBalancer : null + loadBalancerInboundNatRules: contains(ipConfiguration, 'loadBalancerInboundNatRules') ? ipConfiguration.loadBalancerInboundNatRules : null + privateIPAddressVersion: contains(ipConfiguration, 'privateIPAddressVersion') ? ipConfiguration.privateIPAddressVersion : null + virtualNetworkTaps: contains(ipConfiguration, 'virtualNetworkTaps') ? ipConfiguration.virtualNetworkTaps : null + } + }] + } +} + +resource networkInterface_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + } + scope: networkInterface +} + +resource networkInterface_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${networkInterface.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkInterface +} + +module networkInterface_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NIC-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkInterface.id + } +}] + +@description('The name of the deployed resource.') +output name string = networkInterface.name + +@description('The resource ID of the deployed resource.') +output resourceId string = networkInterface.id + +@description('The resource group of the deployed resource.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = networkInterface.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/readme.md new file mode 100644 index 000000000..ee5380da0 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/readme.md @@ -0,0 +1,402 @@ +# Network Interface `[Microsoft.Network/networkInterfaces]` + +This module deploys Network Interfaces. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/networkInterfaces` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkInterfaces) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `ipConfigurations` | array | A list of IPConfigurations of the network interface. | +| `name` | string | The name of the network interface. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `auxiliaryMode` | string | `'None'` | `[Floating, MaxConnections, None]` | Auxiliary mode of Network Interface resource. Not all regions are enabled for Auxiliary Mode Nic. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource identifier of log analytics. | +| `disableTcpStateTracking` | bool | `False` | | Indicates whether to disable tcp state tracking. Subscription must be registered for the Microsoft.Network/AllowDisableTcpStateTracking feature before this property can be set to true. | +| `dnsServers` | array | `[]` | | List of DNS servers IP addresses. Use 'AzureProvidedDNS' to switch to azure provided DNS resolution. 'AzureProvidedDNS' value cannot be combined with other IPs, it must be the only value in dnsServers collection. | +| `enableAcceleratedNetworking` | bool | `False` | | If the network interface is accelerated networking enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableIPForwarding` | bool | `False` | | Indicates whether IP forwarding is enabled on this network interface. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkSecurityGroupResourceId` | string | `''` | | The network security group (NSG) to attach to the network interface. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `ipConfigurations` + +The IP configurations to apply to the network interface. + +```json +{ + "name": "ipconfig01", + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001", + "loadBalancerBackendAddressPools": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/backendAddressPools/servers" + } + ], + "applicationSecurityGroups": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/applicationSecurityGroups/adp-<>-az-asg-x-001" + } + ] +} +``` + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed resource. | +| `resourceGroupName` | string | The resource group of the deployed resource. | +| `resourceId` | string | The resource ID of the deployed resource. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module networkInterfaces './Microsoft.Network/networkInterfaces/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnicom' + params: { + // Required parameters + ipConfigurations: [ + { + applicationSecurityGroups: [ + { + id: '' + } + ] + loadBalancerBackendAddressPools: [ + { + id: '' + } + ] + name: 'ipconfig01' + subnetResourceId: '' + } + { + applicationSecurityGroups: [ + { + id: '' + } + ] + subnetResourceId: '' + } + ] + name: '<>nnicom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "ipConfigurations": { + "value": [ + { + "applicationSecurityGroups": [ + { + "id": "" + } + ], + "loadBalancerBackendAddressPools": [ + { + "id": "" + } + ], + "name": "ipconfig01", + "subnetResourceId": "" + }, + { + "applicationSecurityGroups": [ + { + "id": "" + } + ], + "subnetResourceId": "" + } + ] + }, + "name": { + "value": "<>nnicom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module networkInterfaces './Microsoft.Network/networkInterfaces/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnimin' + params: { + // Required parameters + ipConfigurations: [ + { + name: 'ipconfig01' + subnetResourceId: '' + } + ] + name: '<>nnimin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "ipConfigurations": { + "value": [ + { + "name": "ipconfig01", + "subnetResourceId": "" + } + ] + }, + "name": { + "value": "<>nnimin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkInterfaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..02c04c9e7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkManager.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkManager +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/dependencies.bicep new file mode 100644 index 000000000..f46467f98 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/dependencies.bicep @@ -0,0 +1,96 @@ +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the Hub Virtual Network to create.') +param virtualNetworkHubName string + +@description('Required. The name of the Spoke 1 Virtual Network to create.') +param virtualNetworkSpoke1Name string + +@description('Required. The name of the Spoke 2 Virtual Network to create.') +param virtualNetworkSpoke2Name 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 +} + +var addressPrefixHub = '10.0.0.0/16' +var addressPrefixSpoke1 = '172.16.0.0/12' +var addressPrefixSpoke2 = '192.168.0.0/16' +var subnetName = 'defaultSubnet' + +resource virtualNetworkHub 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkHubName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefixHub + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: addressPrefixHub + } + } + ] + } +} + +resource virtualNetworkSpoke1 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkSpoke1Name + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefixSpoke1 + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: addressPrefixSpoke1 + } + } + ] + } +} + +resource virtualNetworkSpoke2 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkSpoke2Name + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefixSpoke2 + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: addressPrefixSpoke2 + } + } + ] + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Hub Virtual Network.') +output virtualNetworkHubId string = virtualNetworkHub.id + +@description('The resource ID of the created Spoke 1 Virtual Network.') +output virtualNetworkSpoke1Id string = virtualNetworkSpoke1.id + +@description('The resource ID of the created Spoke 2 Virtual Network.') +output virtualNetworkSpoke2Id string = virtualNetworkSpoke2.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/deploy.test.bicep new file mode 100644 index 000000000..8227c6c60 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/.test/common/deploy.test.bicep @@ -0,0 +1,247 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.networkmanagers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nnmcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + virtualNetworkHubName: 'dep-<>-vnetHub-${serviceShort}' + virtualNetworkSpoke1Name: 'dep-<>-vnetSpoke1-${serviceShort}' + virtualNetworkSpoke2Name: 'dep-<>-vnetSpoke2-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +var networkManagerName = '<>${serviceShort}001' +var networkManagerExpecetedResourceID = '${resourceGroup.id}/providers/Microsoft.Network/networkManagers/${networkManagerName}' + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + name: networkManagerName + enableDefaultTelemetry: enableDefaultTelemetry + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + networkManagerScopeAccesses: [ + 'Connectivity' + 'SecurityAdmin' + ] + networkManagerScopes: { + subscriptions: [ + subscription().id + ] + } + networkGroups: [ + { + name: 'network-group-spokes' + description: 'network-group-spokes description' + staticMembers: [ + { + name: 'virtualNetworkSpoke1' + resourceId: nestedDependencies.outputs.virtualNetworkSpoke1Id + } + { + name: 'virtualNetworkSpoke2' + resourceId: nestedDependencies.outputs.virtualNetworkSpoke2Id + } + ] + } + ] + connectivityConfigurations: [ + { + name: 'hubSpokeConnectivity' + description: 'hubSpokeConnectivity description' + connectivityTopology: 'HubAndSpoke' + hubs: [ + { + resourceId: nestedDependencies.outputs.virtualNetworkHubId + resourceType: 'Microsoft.Network/virtualNetworks' + } + ] + deleteExistingPeering: 'True' + isGlobal: 'True' + appliesToGroups: [ + { + networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes' + useHubGateway: 'False' + groupConnectivity: 'None' + isGlobal: 'False' + } + ] + } + { + name: 'MeshConnectivity' + description: 'MeshConnectivity description' + connectivityTopology: 'Mesh' + deleteExistingPeering: 'True' + isGlobal: 'True' + appliesToGroups: [ + { + networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes' + useHubGateway: 'False' + groupConnectivity: 'None' + isGlobal: 'False' + } + ] + } + ] + scopeConnections: [ + { + name: 'scope-connection-test' + description: 'description of the scope connection' + resourceId: subscription().id + tenantid: tenant().tenantId + } + ] + securityAdminConfigurations: [ + { + name: 'test-security-admin-config' + description: 'description of the security admin config' + applyOnNetworkIntentPolicyBasedServices: [ + 'AllowRulesOnly' + ] + ruleCollections: [ + { + name: 'test-rule-collection-1' + description: 'test-rule-collection-description' + appliesToGroups: [ + { + networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes' + } + ] + rules: [ + { + name: 'test-inbound-allow-rule-1' + description: 'test-inbound-allow-rule-1-description' + access: 'Allow' + direction: 'Inbound' + priority: 150 + protocol: 'Tcp' + } + { + name: 'test-outbound-deny-rule-2' + description: 'test-outbound-deny-rule-2-description' + access: 'Deny' + direction: 'Outbound' + priority: 200 + protocol: 'Tcp' + sourcePortRanges: [ + '80' + '442-445' + ] + sources: [ + { + addressPrefix: 'AppService.WestEurope' + addressPrefixType: 'ServiceTag' + } + ] + } + ] + } + { + name: 'test-rule-collection-2' + description: 'test-rule-collection-description' + appliesToGroups: [ + { + networkGroupId: '${networkManagerExpecetedResourceID}/networkGroups/network-group-spokes' + } + ] + rules: [ + { + name: 'test-inbound-allow-rule-3' + description: 'test-inbound-allow-rule-3-description' + access: 'Allow' + direction: 'Inbound' + destinationPortRanges: [ + '80' + '442-445' + ] + destinations: [ + { + addressPrefix: '192.168.20.20' + addressPrefixType: 'IPPrefix' + } + ] + priority: 250 + protocol: 'Tcp' + } + { + name: 'test-inbound-allow-rule-4' + description: 'test-inbound-allow-rule-4-description' + access: 'Allow' + direction: 'Inbound' + sources: [ + { + addressPrefix: '10.0.0.0/24' + addressPrefixType: 'IPPrefix' + } + { + addressPrefix: '100.100.100.100' + addressPrefixType: 'IPPrefix' + } + ] + destinations: [ + { + addressPrefix: '172.16.0.0/24' + addressPrefixType: 'IPPrefix' + } + { + addressPrefix: '172.16.1.0/24' + addressPrefixType: 'IPPrefix' + } + ] + priority: 260 + protocol: 'Tcp' + } + ] + } + ] + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/deploy.bicep new file mode 100644 index 000000000..1cb013c8e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/deploy.bicep @@ -0,0 +1,78 @@ +@sys.description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@maxLength(64) +@sys.description('Required. The name of the connectivity configuration.') +param name string + +@maxLength(500) +@sys.description('Optional. A description of the connectivity configuration.') +param description string = '' + +@sys.description('Required. Network Groups for the configuration.') +param appliesToGroups array = [] + +@allowed([ + 'HubAndSpoke' + 'Mesh' +]) +@sys.description('Required. Connectivity topology type.') +param connectivityTopology string + +@sys.description('Conditional. List of hub items. This will create peerings between the specified hub and the virtual networks in the network group specified. Required if connectivityTopology is of type "HubAndSpoke".') +param hubs array = [] + +@allowed([ + 'True' + 'False' +]) +@sys.description('Optional. Flag if need to remove current existing peerings. If set to "True", all peerings on virtual networks in selected network groups will be removed and replaced with the peerings defined by this configuration. Optional when connectivityTopology is of type "HubAndSpoke".') +param deleteExistingPeering string = 'False' + +@allowed([ + 'True' + 'False' +]) +@sys.description('Optional. Flag if global mesh is supported. By default, mesh connectivity is applied to virtual networks within the same region. If set to "True", a global mesh enables connectivity across regions.') +param isGlobal string = 'False' + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName +} + +resource connectivityConfiguration 'Microsoft.Network/networkManagers/connectivityConfigurations@2022-07-01' = { + name: name + parent: networkManager + properties: { + appliesToGroups: appliesToGroups + connectivityTopology: connectivityTopology + deleteExistingPeering: connectivityTopology == 'HubAndSpoke' ? deleteExistingPeering : 'False' + description: description + hubs: connectivityTopology == 'HubAndSpoke' ? hubs : [] + isGlobal: isGlobal + } +} + +@sys.description('The name of the deployed connectivity configuration.') +output name string = connectivityConfiguration.name + +@sys.description('The resource ID of the deployed connectivity configuration.') +output resourceId string = connectivityConfiguration.id + +@sys.description('The resource group the connectivity configuration was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/readme.md new file mode 100644 index 000000000..190901d42 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/readme.md @@ -0,0 +1,56 @@ +# Network NetworkManagers ConnectivityConfigurations `[Microsoft.Network/networkManagers/connectivityConfigurations]` + +This module deploys Network NetworkManagers ConnectivityConfigurations. +Connectivity configurations define hub-and-spoke or mesh topologies applied to one or more network groups. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/connectivityConfigurations) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `appliesToGroups` | array | | Network Groups for the configuration. | +| `connectivityTopology` | string | `[HubAndSpoke, Mesh]` | Connectivity topology type. | +| `name` | string | | The name of the connectivity configuration. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `hubs` | array | List of hub items. This will create peerings between the specified hub and the virtual networks in the network group specified. Required if connectivityTopology is of type "HubAndSpoke". | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `deleteExistingPeering` | string | `'False'` | `[False, True]` | Flag if need to remove current existing peerings. If set to "True", all peerings on virtual networks in selected network groups will be removed and replaced with the peerings defined by this configuration. Optional when connectivityTopology is of type "HubAndSpoke". | +| `description` | string | `''` | | A description of the connectivity configuration. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `isGlobal` | string | `'False'` | `[False, True]` | Flag if global mesh is supported. By default, mesh connectivity is applied to virtual networks within the same region. If set to "True", a global mesh enables connectivity across regions. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed connectivity configuration. | +| `resourceGroupName` | string | The resource group the connectivity configuration was deployed into. | +| `resourceId` | string | The resource ID of the deployed connectivity configuration. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/connectivityConfigurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/deploy.bicep new file mode 100644 index 000000000..01051aec1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/deploy.bicep @@ -0,0 +1,157 @@ +@sys.description('Required. Name of the Network Manager.') +@minLength(1) +@maxLength(64) +param name string + +@sys.description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@sys.description('Optional. Specify the type of lock.') +param lock string = '' + +@sys.description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@sys.description('Optional. Tags of the resource.') +param tags object = {} + +@maxLength(500) +@sys.description('Optional. A description of the network manager.') +param description string = '' + +@sys.description('Required. Scope Access. String array containing any of "Connectivity", "SecurityAdmin". The connectivity feature allows you to create network topologies at scale. The security admin feature lets you create high-priority security rules, which take precedence over NSGs.') +param networkManagerScopeAccesses array + +@sys.description('Required. Scope of Network Manager. Contains a list of management groups or a list of subscriptions. This defines the boundary of network resources that this Network Manager instance can manage. If using Management Groups, ensure that the "Microsoft.Network" resource provider is registered for those Management Groups prior to deployment.') +param networkManagerScopes object + +@sys.description('Conditional. Network Groups and static members to create for the network manager. Required if using "connectivityConfigurations" or "securityAdminConfigurations" parameters.') +param networkGroups array = [] + +@sys.description('Optional. Connectivity Configurations to create for the network manager. Network manager must contain at least one network group in order to define connectivity configurations.') +param connectivityConfigurations array = [] + +@sys.description('Optional. Scope Connections to create for the network manager. Allows network manager to manage resources from another tenant.') +param scopeConnections array = [] + +@sys.description('Optional. Security Admin Configurations, Rule Collections and Rules to create for the network manager.') +param securityAdminConfigurations array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + description: description + networkManagerScopeAccesses: networkManagerScopeAccesses + networkManagerScopes: networkManagerScopes + } +} + +module networkManager_networkGroups 'networkGroups/deploy.bicep' = [for (networkGroup, index) in networkGroups: { + name: '${uniqueString(deployment().name, location)}-NetworkManager-NetworkGroups-${index}' + params: { + name: networkGroup.name + networkManagerName: networkManager.name + description: contains(networkGroup, 'description') ? networkGroup.description : '' + staticMembers: contains(networkGroup, 'staticMembers') ? networkGroup.staticMembers : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module networkManager_connectivityConfigurations 'connectivityConfigurations/deploy.bicep' = [for (connectivityConfiguration, index) in connectivityConfigurations: { + name: '${uniqueString(deployment().name, location)}-NetworkManager-ConnectivityConfigurations-${index}' + params: { + name: connectivityConfiguration.name + networkManagerName: networkManager.name + description: contains(connectivityConfiguration, 'description') ? connectivityConfiguration.description : '' + appliesToGroups: connectivityConfiguration.appliesToGroups + connectivityTopology: connectivityConfiguration.connectivityTopology + hubs: contains(connectivityConfiguration, 'hubs') ? connectivityConfiguration.hubs : [] + deleteExistingPeering: contains(connectivityConfiguration, 'hubs') && (connectivityConfiguration.connectivityTopology == 'HubAndSpoke') ? connectivityConfiguration.deleteExistingPeering : 'False' + isGlobal: contains(connectivityConfiguration, 'isGlobal') ? connectivityConfiguration.isGlobal : 'False' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: networkManager_networkGroups +}] + +module networkManager_scopeConnections 'scopeConnections/deploy.bicep' = [for (scopeConnection, index) in scopeConnections: { + name: '${uniqueString(deployment().name, location)}-NetworkManager-ScopeConnections-${index}' + params: { + name: scopeConnection.name + networkManagerName: networkManager.name + description: contains(scopeConnection, 'description') ? scopeConnection.description : '' + resourceId: scopeConnection.resourceId + tenantId: scopeConnection.tenantId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module networkManager_securityAdminConfigurations 'securityAdminConfigurations/deploy.bicep' = [for (securityAdminConfiguration, index) in securityAdminConfigurations: { + name: '${uniqueString(deployment().name, location)}-NetworkManager-SecurityAdminConfigurations-${index}' + params: { + name: securityAdminConfiguration.name + networkManagerName: networkManager.name + description: contains(securityAdminConfiguration, 'description') ? securityAdminConfiguration.description : '' + applyOnNetworkIntentPolicyBasedServices: securityAdminConfiguration.applyOnNetworkIntentPolicyBasedServices + ruleCollections: contains(securityAdminConfiguration, 'ruleCollections') ? securityAdminConfiguration.ruleCollections : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: networkGroups +}] + +resource networkManager_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${networkManager.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkManager +} + +module networkManager_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NetworkManager-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkManager.id + } +}] + +@sys.description('The resource group the network manager was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The resource ID of the network manager.') +output resourceId string = networkManager.id + +@sys.description('The name of the network manager.') +output name string = networkManager.name + +@sys.description('The location the resource was deployed into.') +output location string = networkManager.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/deploy.bicep new file mode 100644 index 000000000..fb98e46ec --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/deploy.bicep @@ -0,0 +1,62 @@ +@sys.description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@maxLength(64) +@sys.description('Required. The name of the network group.') +param name string + +@maxLength(500) +@sys.description('Optional. A description of the network group.') +param description string = '' + +@sys.description('Optional. Static Members to create for the network group. Contains virtual networks to add to the network group.') +param staticMembers array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName +} + +resource networkGroup 'Microsoft.Network/networkManagers/networkGroups@2022-07-01' = { + name: name + parent: networkManager + properties: { + description: description + } +} + +module networkGroup_staticMembers 'staticMembers/deploy.bicep' = [for (staticMember, index) in staticMembers: { + name: '${uniqueString(deployment().name)}-NetworkGroup-StaticMembers-${index}' + params: { + networkManagerName: networkManager.name + networkGroupName: networkGroup.name + name: staticMember.name + resourceId: staticMember.resourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@sys.description('The name of the deployed network group.') +output name string = networkGroup.name + +@sys.description('The resource ID of the deployed network group.') +output resourceId string = networkGroup.id + +@sys.description('The resource group the network group was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/readme.md new file mode 100644 index 000000000..d3dbd8b10 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/readme.md @@ -0,0 +1,53 @@ +# Network NetworkManagers NetworkGroups `[Microsoft.Network/networkManagers/networkGroups]` + +This module deploys Network NetworkManagers NetworkGroups. +A network group is a collection of same-type network resources that you can associate with network manager configurations. You can add same-type network resources after you create the network group. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/networkGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/networkGroups) | +| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/networkGroups/staticMembers) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the network group. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | A description of the network group. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `staticMembers` | _[staticMembers](staticMembers/readme.md)_ array | `[]` | Static Members to create for the network group. Contains virtual networks to add to the network group. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed network group. | +| `resourceGroupName` | string | The resource group the network group was deployed into. | +| `resourceId` | string | The resource ID of the deployed network group. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/deploy.bicep new file mode 100644 index 000000000..96b201f9c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/deploy.bicep @@ -0,0 +1,51 @@ +@description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@description('Conditional. The name of the parent network group. Required if the template is used in a standalone deployment.') +param networkGroupName string + +@description('Required. The name of the static member.') +param name string + +@description('Required. Resource ID of the virtual network.') +param resourceId string + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName + + resource networkGroup 'networkGroups@2022-07-01' existing = { + name: networkGroupName + } +} + +resource staticMember 'Microsoft.Network/networkManagers/networkGroups/staticMembers@2022-07-01' = { + name: name + parent: networkManager::networkGroup + properties: { + resourceId: resourceId + } +} + +@description('The name of the deployed static member.') +output name string = staticMember.name + +@description('The resource ID of the deployed static member.') +output resourceId string = staticMember.id + +@description('The resource group the static member was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/readme.md new file mode 100644 index 000000000..8c9fb3af1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/readme.md @@ -0,0 +1,52 @@ +# Network NetworkManagers NetworkGroups StaticMembers `[Microsoft.Network/networkManagers/networkGroups/staticMembers]` + +This module deploys Network NetworkManagers NetworkGroups StaticMembers. +Static membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/networkGroups/staticMembers) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the static member. | +| `resourceId` | string | Resource ID of the virtual network. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkGroupName` | string | The name of the parent network group. Required if the template is used in a standalone deployment. | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed static member. | +| `resourceGroupName` | string | The resource group the static member was deployed into. | +| `resourceId` | string | The resource ID of the deployed static member. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/staticMembers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/networkGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/readme.md new file mode 100644 index 000000000..aaee801e6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/readme.md @@ -0,0 +1,987 @@ +# Network NetworkManagers `[Microsoft.Network/networkManagers]` + +This module deploys Network NetworkManagers. +Azure Virtual Network Manager is a management service that enables you to group, configure, deploy, and manage virtual networks globally across subscriptions. With Virtual Network Manager, you can define network groups to identify and logically segment your virtual networks. Then you can determine the connectivity and security configurations you want and apply them across all the selected virtual networks in network groups at once. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Considerations](#Considerations) +- [Deployment examples](#Deployment-examples) + +## 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/networkManagers` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers) | +| `Microsoft.Network/networkManagers/connectivityConfigurations` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/connectivityConfigurations) | +| `Microsoft.Network/networkManagers/networkGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/networkGroups) | +| `Microsoft.Network/networkManagers/networkGroups/staticMembers` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/networkGroups/staticMembers) | +| `Microsoft.Network/networkManagers/scopeConnections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/scopeConnections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Network Manager. | +| `networkManagerScopeAccesses` | array | Scope Access. String array containing any of "Connectivity", "SecurityAdmin". The connectivity feature allows you to create network topologies at scale. The security admin feature lets you create high-priority security rules, which take precedence over NSGs. | +| `networkManagerScopes` | object | Scope of Network Manager. Contains a list of management groups or a list of subscriptions. This defines the boundary of network resources that this Network Manager instance can manage. If using Management Groups, ensure that the "Microsoft.Network" resource provider is registered for those Management Groups prior to deployment. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkGroups` | _[networkGroups](networkGroups/readme.md)_ array | Network Groups and static members to create for the network manager. Required if using "connectivityConfigurations" or "securityAdminConfigurations" parameters. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `connectivityConfigurations` | _[connectivityConfigurations](connectivityConfigurations/readme.md)_ array | `[]` | | Connectivity Configurations to create for the network manager. Network manager must contain at least one network group in order to define connectivity configurations. | +| `description` | string | `''` | | A description of the network manager. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `scopeConnections` | _[scopeConnections](scopeConnections/readme.md)_ array | `[]` | | Scope Connections to create for the network manager. Allows network manager to manage resources from another tenant. | +| `securityAdminConfigurations` | _[securityAdminConfigurations](securityAdminConfigurations/readme.md)_ array | `[]` | | Security Admin Configurations, Rule Collections and Rules to create for the network manager. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `` + +Features are scope access that you allow the Azure Virtual Network Manager to manage. Azure Virtual Network Manager currently has two feature scopes, which are `Connectivity` and `SecurityAdmin`. You can enable both feature scopes on the same Virtual Network Manager instance. + +

+ +Parameter JSON format + +```json +"networkManagerScopeAccesses": { + "value": [ + "Connectivity" + "SecurityAdmin" + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +networkManagerScopeAccesses: [ + 'Connectivity' + 'SecurityAdmin' +] +``` + +
+

+ +### Parameter Usage: `` + +Contains a list of management groups or a list of subscriptions. This defines the boundary of network resources that this virtual network manager instance can manage. + +**Note**: You can't create multiple Azure Virtual Network Manager instances with an overlapping scope of the same hierarchy and the same features selected. + +

+ +Parameter JSON format + +```json +"networkManagerScopes": { + "value": { + "subscriptions": [ + "/subscriptions/" + ], + "managementGroups": [ + "/providers/Microsoft.Management/managementGroups/" + ] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +networkManagerScopes: { + subscriptions: [ + '/subscriptions/' + ] + managementGroups: [ + '/providers/Microsoft.Management/managementGroups/<>' + ] +} +``` + +
+

+ +### Parameter Usage: `` + +A network group is global container that includes a set of virtual network resources from any region. Then, configurations are applied to target the network group, which applies the configuration to all members of the group. The two types are group memberships are static and dynamic memberships. Static membership allows you to explicitly add virtual networks to a group by manually selecting individual virtual networks, and is available as a child module, while dynamic membership is defined through Azure policy. See [How Azure Policy works with Network Groups](https://learn.microsoft.com/en-us/azure/virtual-network-manager/concept-azure-policy-integration) for more details. + +

+ +Parameter JSON format + +```json +"networkGroups": { + "value": [ + { + "name": "network-group-test", + "description": "network-group-test description", + "staticMembers": [ + { + "name": "vnet1", + "resourceId": "" + }, + { + "name": "vnet2", + "resourceId": "" + } + ] + } + ] +}, +``` + +
+ +
+ +Bicep format + +```bicep +networkGroups: [ + { + name: 'network-group-test' + description: 'network-group-test description' + staticMembers: [ + { + name: 'vnet1' + resourceId: '' + } + { + name: 'vnet2' + resourceId: '' + } + ] + } +] +``` + +
+

+ +### Parameter Usage: `` + +Connectivity configurations allow you to create different network topologies based on your network needs. You have two topologies to choose from, a mesh network and a hub and spoke. Connectivities between virtual networks are defined within the configuration settings. + +

+ +Parameter JSON format + +```json +"connectivityConfigurations": { + "value": [ + { + "name": "hubSpokeConnectivity", + "description": "hubSpokeConnectivity description", + "connectivityTopology": "HubAndSpoke", + "hubs": [ + { + "resourceId": "", + "resourceType": "Microsoft.Network/virtualNetworks" + } + ], + "deleteExistingPeering": "True", + "isGlobal": "True", + "appliesToGroups": [ + { + "networkGroupId": "", + "useHubGateway": "False", + "groupConnectivity": "None", + "isGlobal": "False" + } + ] + }, + { + "name": "MeshConnectivity", + "description": "MeshConnectivity description", + "connectivityTopology": "Mesh", + "deleteExistingPeering": "True", + "isGlobal": "True", + "appliesToGroups": [ + { + "networkGroupId": "", + "useHubGateway": "False", + "groupConnectivity": "None", + "isGlobal": "False" + } + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +connectivityConfigurations: [ + { + name: 'hubSpokeConnectivity' + description: 'hubSpokeConnectivity description' + connectivityTopology: 'HubAndSpoke' + hubs: [ + { + resourceId: '' + resourceType: 'Microsoft.Network/virtualNetworks' + } + ] + deleteExistingPeering: 'True' + isGlobal: 'True' + appliesToGroups: [ + { + networkGroupId: '' + useHubGateway: 'False' + groupConnectivity: 'None' + isGlobal: 'False' + } + ] + } + { + name: 'MeshConnectivity' + description: 'MeshConnectivity description' + connectivityTopology: 'Mesh' + deleteExistingPeering: 'True' + isGlobal: 'True' + appliesToGroups: [ + { + networkGroupId: '' + useHubGateway: 'False' + groupConnectivity: 'None' + isGlobal: 'False' + } + ] + } +] +``` + +
+

+ +### Parameter Usage: `` + +Scope Connections to create for the network manager. Allows network manager to manage resources from another tenant. Supports management groups or subscriptions from another tenant. + +

+ +Parameter JSON format + +```json +"scopeConnections": { + "value": [ + { + "name": "scope-connection-test", + "description": "description of the scope connection", + "resourceId": "/subscriptions/", // or "/providers/Microsoft.Management/managementGroups/" + "tenantid": "" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +scopeConnections: [ + { + name: 'scope-connection-test' + description: 'description of the scope connection' + resourceId: '/subscriptions/', // or '/providers/Microsoft.Management/managementGroups/' + tenantid: t'' + } +] +``` + +
+

+ +### Parameter Usage: `` + +Azure Virtual Network Manager provides two different types of configurations you can deploy across your virtual networks, one of them being a SecurityAdmin configuration. A security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. You then associate the rule collection with the network groups that you want to apply the security admin rules to. + +

+ +Parameter JSON format + +```json +"securityAdminConfigurations": { + "value": [ + { + "name": "test-security-admin-config", + "description": "description of the security admin config", + "applyOnNetworkIntentPolicyBasedServices": [ + "AllowRulesOnly" + ], + "ruleCollections": [ + { + "name": "test-rule-collection-1", + "description": "test-rule-collection-description", + "appliesToGroups": [ + { + "networkGroupId": "" + } + ], + "rules": [ + { + "name": "test-inbound-allow-rule-1", + "description": "test-inbound-allow-rule-1-description", + "access": "Allow", + "direction": "Inbound", + "priority": 150, + "protocol": "Tcp" + } + ] + } + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +securityAdminConfigurations: [ + { + name: 'test-security-admin-config' + description: 'description of the security admin config' + applyOnNetworkIntentPolicyBasedServices: [ + 'AllowRulesOnly' + ] + ruleCollections: [ + { + name: 'test-rule-collection-1' + description: 'test-rule-collection-description' + appliesToGroups: [ + { + networkGroupId: '' + } + ] + rules: [ + { + name: 'test-inbound-allow-rule-1' + description: 'test-inbound-allow-rule-1-description' + access: 'Allow' + direction: 'Inbound' + priority: 150 + protocol: 'Tcp' + } + ] + } + ] + } +] +``` + +
+

+ + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the network manager. | +| `resourceGroupName` | string | The resource group the network manager was deployed into. | +| `resourceId` | string | The resource ID of the network manager. | + +## Cross-referenced modules + +_None_ + +## Considerations + +In order to deploy a Network Manager with the `networkManagerScopes` property set to `managementGroups`, you need to register the `Microsoft.Network` resource provider at the Management Group first ([ref](https://learn.microsoft.com/en-us/rest/api/resources/providers/register-at-management-group-scope)). + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module networkManagers './Microsoft.Network/networkManagers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnmcom' + params: { + // Required parameters + name: '' + networkManagerScopeAccesses: [ + 'Connectivity' + 'SecurityAdmin' + ] + networkManagerScopes: { + subscriptions: [ + '' + ] + } + // Non-required parameters + connectivityConfigurations: [ + { + appliesToGroups: [ + { + groupConnectivity: 'None' + isGlobal: 'False' + networkGroupId: '' + useHubGateway: 'False' + } + ] + connectivityTopology: 'HubAndSpoke' + deleteExistingPeering: 'True' + description: 'hubSpokeConnectivity description' + hubs: [ + { + resourceId: '' + resourceType: 'Microsoft.Network/virtualNetworks' + } + ] + isGlobal: 'True' + name: 'hubSpokeConnectivity' + } + { + appliesToGroups: [ + { + groupConnectivity: 'None' + isGlobal: 'False' + networkGroupId: '' + useHubGateway: 'False' + } + ] + connectivityTopology: 'Mesh' + deleteExistingPeering: 'True' + description: 'MeshConnectivity description' + isGlobal: 'True' + name: 'MeshConnectivity' + } + ] + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + networkGroups: [ + { + description: 'network-group-spokes description' + name: 'network-group-spokes' + staticMembers: [ + { + name: 'virtualNetworkSpoke1' + resourceId: '' + } + { + name: 'virtualNetworkSpoke2' + resourceId: '' + } + ] + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + scopeConnections: [ + { + description: 'description of the scope connection' + name: 'scope-connection-test' + resourceId: '' + tenantid: '' + } + ] + securityAdminConfigurations: [ + { + applyOnNetworkIntentPolicyBasedServices: [ + 'AllowRulesOnly' + ] + description: 'description of the security admin config' + name: 'test-security-admin-config' + ruleCollections: [ + { + appliesToGroups: [ + { + networkGroupId: '' + } + ] + description: 'test-rule-collection-description' + name: 'test-rule-collection-1' + rules: [ + { + access: 'Allow' + description: 'test-inbound-allow-rule-1-description' + direction: 'Inbound' + name: 'test-inbound-allow-rule-1' + priority: 150 + protocol: 'Tcp' + } + { + access: 'Deny' + description: 'test-outbound-deny-rule-2-description' + direction: 'Outbound' + name: 'test-outbound-deny-rule-2' + priority: 200 + protocol: 'Tcp' + sourcePortRanges: [ + '442-445' + '80' + ] + sources: [ + { + addressPrefix: 'AppService.WestEurope' + addressPrefixType: 'ServiceTag' + } + ] + } + ] + } + { + appliesToGroups: [ + { + networkGroupId: '' + } + ] + description: 'test-rule-collection-description' + name: 'test-rule-collection-2' + rules: [ + { + access: 'Allow' + description: 'test-inbound-allow-rule-3-description' + destinationPortRanges: [ + '442-445' + '80' + ] + destinations: [ + { + addressPrefix: '192.168.20.20' + addressPrefixType: 'IPPrefix' + } + ] + direction: 'Inbound' + name: 'test-inbound-allow-rule-3' + priority: 250 + protocol: 'Tcp' + } + { + access: 'Allow' + description: 'test-inbound-allow-rule-4-description' + destinations: [ + { + addressPrefix: '172.16.0.0/24' + addressPrefixType: 'IPPrefix' + } + { + addressPrefix: '172.16.1.0/24' + addressPrefixType: 'IPPrefix' + } + ] + direction: 'Inbound' + name: 'test-inbound-allow-rule-4' + priority: 260 + protocol: 'Tcp' + sources: [ + { + addressPrefix: '10.0.0.0/24' + addressPrefixType: 'IPPrefix' + } + { + addressPrefix: '100.100.100.100' + addressPrefixType: 'IPPrefix' + } + ] + } + ] + } + ] + } + ] + tags: { + Environment: 'Non-Prod' + 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": "" + }, + "networkManagerScopeAccesses": { + "value": [ + "Connectivity", + "SecurityAdmin" + ] + }, + "networkManagerScopes": { + "value": { + "subscriptions": [ + "" + ] + } + }, + // Non-required parameters + "connectivityConfigurations": { + "value": [ + { + "appliesToGroups": [ + { + "groupConnectivity": "None", + "isGlobal": "False", + "networkGroupId": "", + "useHubGateway": "False" + } + ], + "connectivityTopology": "HubAndSpoke", + "deleteExistingPeering": "True", + "description": "hubSpokeConnectivity description", + "hubs": [ + { + "resourceId": "", + "resourceType": "Microsoft.Network/virtualNetworks" + } + ], + "isGlobal": "True", + "name": "hubSpokeConnectivity" + }, + { + "appliesToGroups": [ + { + "groupConnectivity": "None", + "isGlobal": "False", + "networkGroupId": "", + "useHubGateway": "False" + } + ], + "connectivityTopology": "Mesh", + "deleteExistingPeering": "True", + "description": "MeshConnectivity description", + "isGlobal": "True", + "name": "MeshConnectivity" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "networkGroups": { + "value": [ + { + "description": "network-group-spokes description", + "name": "network-group-spokes", + "staticMembers": [ + { + "name": "virtualNetworkSpoke1", + "resourceId": "" + }, + { + "name": "virtualNetworkSpoke2", + "resourceId": "" + } + ] + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "scopeConnections": { + "value": [ + { + "description": "description of the scope connection", + "name": "scope-connection-test", + "resourceId": "", + "tenantid": "" + } + ] + }, + "securityAdminConfigurations": { + "value": [ + { + "applyOnNetworkIntentPolicyBasedServices": [ + "AllowRulesOnly" + ], + "description": "description of the security admin config", + "name": "test-security-admin-config", + "ruleCollections": [ + { + "appliesToGroups": [ + { + "networkGroupId": "" + } + ], + "description": "test-rule-collection-description", + "name": "test-rule-collection-1", + "rules": [ + { + "access": "Allow", + "description": "test-inbound-allow-rule-1-description", + "direction": "Inbound", + "name": "test-inbound-allow-rule-1", + "priority": 150, + "protocol": "Tcp" + }, + { + "access": "Deny", + "description": "test-outbound-deny-rule-2-description", + "direction": "Outbound", + "name": "test-outbound-deny-rule-2", + "priority": 200, + "protocol": "Tcp", + "sourcePortRanges": [ + "442-445", + "80" + ], + "sources": [ + { + "addressPrefix": "AppService.WestEurope", + "addressPrefixType": "ServiceTag" + } + ] + } + ] + }, + { + "appliesToGroups": [ + { + "networkGroupId": "" + } + ], + "description": "test-rule-collection-description", + "name": "test-rule-collection-2", + "rules": [ + { + "access": "Allow", + "description": "test-inbound-allow-rule-3-description", + "destinationPortRanges": [ + "442-445", + "80" + ], + "destinations": [ + { + "addressPrefix": "192.168.20.20", + "addressPrefixType": "IPPrefix" + } + ], + "direction": "Inbound", + "name": "test-inbound-allow-rule-3", + "priority": 250, + "protocol": "Tcp" + }, + { + "access": "Allow", + "description": "test-inbound-allow-rule-4-description", + "destinations": [ + { + "addressPrefix": "172.16.0.0/24", + "addressPrefixType": "IPPrefix" + }, + { + "addressPrefix": "172.16.1.0/24", + "addressPrefixType": "IPPrefix" + } + ], + "direction": "Inbound", + "name": "test-inbound-allow-rule-4", + "priority": 260, + "protocol": "Tcp", + "sources": [ + { + "addressPrefix": "10.0.0.0/24", + "addressPrefixType": "IPPrefix" + }, + { + "addressPrefix": "100.100.100.100", + "addressPrefixType": "IPPrefix" + } + ] + } + ] + } + ] + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/deploy.bicep new file mode 100644 index 000000000..055e0c136 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/deploy.bicep @@ -0,0 +1,54 @@ +@sys.description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@maxLength(64) +@sys.description('Required. The name of the scope connection.') +param name string + +@maxLength(500) +@sys.description('Optional. A description of the scope connection.') +param description string = '' + +@sys.description('Required. Enter the subscription or management group resource ID that you want to add to this network manager\'s scope.') +param resourceId string + +@sys.description('Required. Tenant ID of the subscription or management group that you want to manage.') +param tenantId string + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName +} + +resource scopeConnection 'Microsoft.Network/networkManagers/scopeConnections@2022-07-01' = { + name: name + parent: networkManager + properties: { + description: description + resourceId: resourceId + tenantId: tenantId + } +} + +@sys.description('The name of the deployed scope connection.') +output name string = scopeConnection.name + +@sys.description('The resource ID of the deployed scope connection.') +output resourceId string = scopeConnection.id + +@sys.description('The resource group the scope connection was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/readme.md new file mode 100644 index 000000000..3745417d5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/readme.md @@ -0,0 +1,53 @@ +# Network NetworkManagers ScopeConnections `[Microsoft.Network/networkManagers/scopeConnections]` + +This module deploys Network NetworkManagers ScopeConnections. +Create a cross-tenant connection to manage a resource from another tenant. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/scopeConnections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/scopeConnections) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the scope connection. | +| `resourceId` | string | Enter the subscription or management group resource ID that you want to add to this network manager's scope. | +| `tenantId` | string | Tenant ID of the subscription or management group that you want to manage. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | A description of the scope connection. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed scope connection. | +| `resourceGroupName` | string | The resource group the scope connection was deployed into. | +| `resourceId` | string | The resource ID of the deployed scope connection. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/scopeConnections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/deploy.bicep new file mode 100644 index 000000000..9dc4a7c48 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/deploy.bicep @@ -0,0 +1,72 @@ +@sys.description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@maxLength(64) +@sys.description('Required. The name of the security admin configuration.') +param name string + +@maxLength(500) +@sys.description('Optional. A description of the security admin configuration.') +param description string = '' + +@allowed([ + 'None' + 'All' + 'AllowRulesOnly' +]) +@sys.description('Required. Enum list of network intent policy based services.') +param applyOnNetworkIntentPolicyBasedServices array = [ 'None' ] + +@sys.description('Optional. A security admin configuration contains a set of rule collections that are applied to network groups. Each rule collection contains one or more security admin rules.') +param ruleCollections array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName +} + +resource securityAdminConfigurations 'Microsoft.Network/networkManagers/securityAdminConfigurations@2022-07-01' = { + name: name + parent: networkManager + properties: { + description: description + applyOnNetworkIntentPolicyBasedServices: applyOnNetworkIntentPolicyBasedServices + } +} + +module securityAdminConfigurations_ruleCollections 'ruleCollections/deploy.bicep' = [for (ruleCollection, index) in ruleCollections: { + name: '${uniqueString(deployment().name)}-SecurityAdminConfigurations-RuleCollections-${index}' + params: { + networkManagerName: networkManager.name + securityAdminConfigurationName: securityAdminConfigurations.name + name: ruleCollection.name + appliesToGroups: ruleCollection.appliesToGroups + rules: contains(ruleCollection, 'rules') ? ruleCollection.rules : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@sys.description('The name of the deployed security admin configuration.') +output name string = securityAdminConfigurations.name + +@sys.description('The resource ID of the deployed security admin configuration.') +output resourceId string = securityAdminConfigurations.id + +@sys.description('The resource group the security admin configuration was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/readme.md new file mode 100644 index 000000000..98b9f49c5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/readme.md @@ -0,0 +1,55 @@ +# Network NetworkManagers SecurityAdminConfigurations `[Microsoft.Network/networkManagers/securityAdminConfigurations]` + +This module deploys Network NetworkManagers SecurityAdminConfigurations. +A security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/securityAdminConfigurations` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `applyOnNetworkIntentPolicyBasedServices` | array | `[None]` | `[All, AllowRulesOnly, None]` | Enum list of network intent policy based services. | +| `name` | string | | | The name of the security admin configuration. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | A description of the security admin configuration. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ruleCollections` | _[ruleCollections](ruleCollections/readme.md)_ array | `[]` | A security admin configuration contains a set of rule collections that are applied to network groups. Each rule collection contains one or more security admin rules. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed security admin configuration. | +| `resourceGroupName` | string | The resource group the security admin configuration was deployed into. | +| `resourceId` | string | The resource ID of the deployed security admin configuration. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/deploy.bicep new file mode 100644 index 000000000..339d59e80 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/deploy.bicep @@ -0,0 +1,82 @@ +@sys.description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@sys.description('Conditional. The name of the parent security admin configuration. Required if the template is used in a standalone deployment.') +param securityAdminConfigurationName string + +@maxLength(64) +@sys.description('Required. The name of the admin rule collection.') +param name string + +@maxLength(500) +@sys.description('Optional. A description of the admin rule collection.') +param description string = '' + +@sys.description('Required. List of network groups for configuration. An admin rule collection must be associated to at least one network group.') +param appliesToGroups array + +@sys.description('Optional. List of rules for the admin rules collection. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail.') +param rules array + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName + + resource securityAdminConfiguration 'securityAdminConfigurations@2022-07-01' existing = { + name: securityAdminConfigurationName + } +} + +resource ruleCollection 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections@2022-07-01' = { + name: name + parent: networkManager::securityAdminConfiguration + properties: { + description: description + appliesToGroups: appliesToGroups + } +} + +module securityAdminConfigurations_rules 'rules/deploy.bicep' = [for (rule, index) in rules: { + name: '${uniqueString(deployment().name)}-RuleCollections-Rules-${index}' + params: { + networkManagerName: networkManager.name + securityAdminConfigurationName: securityAdminConfigurationName + ruleCollectionName: ruleCollection.name + name: rule.name + access: rule.access + description: contains(rule, 'description') ? rule.description : '' + destinationPortRanges: contains(rule, 'destinationPortRanges') ? rule.destinationPortRanges : [] + destinations: contains(rule, 'destinations') ? rule.destinations : [] + direction: rule.direction + priority: rule.priority + protocol: rule.protocol + sourcePortRanges: contains(rule, 'sourcePortRanges') ? rule.sourcePortRanges : [] + sources: contains(rule, 'sources') ? rule.sources : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@sys.description('The name of the deployed admin rule collection.') +output name string = ruleCollection.name + +@sys.description('The resource ID of the deployed admin rule collection.') +output resourceId string = ruleCollection.id + +@sys.description('The resource group the admin rule collection was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/readme.md new file mode 100644 index 000000000..e2bb5fc28 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/readme.md @@ -0,0 +1,55 @@ +# Network NetworkManagers SecurityAdminConfigurations RuleCollections `[Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections]` + +This module deploys Network NetworkManagers SecurityAdminConfigurations RuleCollections. +A security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections) | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `appliesToGroups` | array | List of network groups for configuration. An admin rule collection must be associated to at least one network group. | +| `name` | string | The name of the admin rule collection. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | +| `securityAdminConfigurationName` | string | The name of the parent security admin configuration. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | A description of the admin rule collection. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `rules` | _[rules](rules/readme.md)_ array | | List of rules for the admin rules collection. Security admin rules allows enforcing security policy criteria that matches the conditions set. Warning: A rule collection without rule will cause a deployment configuration for security admin goal state in network manager to fail. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed admin rule collection. | +| `resourceGroupName` | string | The resource group the admin rule collection was deployed into. | +| `resourceId` | string | The resource ID of the deployed admin rule collection. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/deploy.bicep new file mode 100644 index 000000000..675d47f32 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/deploy.bicep @@ -0,0 +1,112 @@ +@sys.description('Conditional. The name of the parent network manager. Required if the template is used in a standalone deployment.') +param networkManagerName string + +@sys.description('Conditional. The name of the parent security admin configuration. Required if the template is used in a standalone deployment.') +param securityAdminConfigurationName string + +@sys.description('Conditional. The name of the parent rule collection. Required if the template is used in a standalone deployment.') +param ruleCollectionName string + +@maxLength(64) +@sys.description('Required. The name of the rule.') +param name string + +@maxLength(500) +@sys.description('Optional. A description of the rule.') +param description string = '' + +@allowed([ + 'Allow' + 'AlwaysAllow' + 'Deny' +]) +@sys.description('Required. Indicates the access allowed for this particular rule. "Allow" means traffic matching this rule will be allowed. "Deny" means traffic matching this rule will be blocked. "AlwaysAllow" means that traffic matching this rule will be allowed regardless of other rules with lower priority or user-defined NSGs.') +param access string + +@sys.description('Optional. List of destination port ranges. This specifies on which ports traffic will be allowed or denied by this rule. Provide an (*) to allow traffic on any port. Port ranges are between 1-65535.') +param destinationPortRanges array = [] + +@sys.description('Optional. The destnations filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted.') +param destinations array = [] + +@allowed([ + 'Inbound' + 'Outbound' +]) +@sys.description('Required. Indicates if the traffic matched against the rule in inbound or outbound.') +param direction string + +@minValue(1) +@maxValue(4096) +@sys.description('Required. The priority of the rule. The value can be between 1 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule.') +param priority int + +@allowed([ + 'Ah' + 'Any' + 'Esp' + 'Icmp' + 'Tcp' + 'Udp' +]) +@sys.description('Required. Network protocol this rule applies to.') +param protocol string + +@sys.description('Optional. List of destination port ranges. This specifies on which ports traffic will be allowed or denied by this rule. Provide an (*) to allow traffic on any port. Port ranges are between 1-65535.') +param sourcePortRanges array = [] + +@sys.description('Optional. The source filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted.') +param sources array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkManager 'Microsoft.Network/networkManagers@2022-07-01' existing = { + name: networkManagerName + + resource securityAdminConfiguration 'securityAdminConfigurations@2022-07-01' existing = { + name: securityAdminConfigurationName + + resource ruleCollection 'ruleCollections@2022-07-01' existing = { + name: ruleCollectionName + } + } +} + +resource rule 'Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules@2022-07-01' = { + name: name + parent: networkManager::securityAdminConfiguration::ruleCollection + kind: 'Custom' + properties: { + access: access + description: description + destinationPortRanges: destinationPortRanges + destinations: destinations + direction: direction + priority: priority + protocol: protocol + sourcePortRanges: sourcePortRanges + sources: sources + } +} + +@sys.description('The name of the deployed rule.') +output name string = rule.name + +@sys.description('The resource ID of the deployed rule.') +output resourceId string = rule.id + +@sys.description('The resource group the rule was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/readme.md new file mode 100644 index 000000000..c059b80a3 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/readme.md @@ -0,0 +1,61 @@ +# Network NetworkManagers SecurityAdminConfigurations RuleCollections Rules `[Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules]` + +This module deploys Network NetworkManagers SecurityAdminConfigurations RuleCollections Rules. +A security admin configuration contains a set of rule collections. Each rule collection contains one or more security admin rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkManagers/securityAdminConfigurations/ruleCollections/rules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `access` | string | `[Allow, AlwaysAllow, Deny]` | Indicates the access allowed for this particular rule. "Allow" means traffic matching this rule will be allowed. "Deny" means traffic matching this rule will be blocked. "AlwaysAllow" means that traffic matching this rule will be allowed regardless of other rules with lower priority or user-defined NSGs. | +| `direction` | string | `[Inbound, Outbound]` | Indicates if the traffic matched against the rule in inbound or outbound. | +| `name` | string | | The name of the rule. | +| `priority` | int | | The priority of the rule. The value can be between 1 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule. | +| `protocol` | string | `[Ah, Any, Esp, Icmp, Tcp, Udp]` | Network protocol this rule applies to. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkManagerName` | string | The name of the parent network manager. Required if the template is used in a standalone deployment. | +| `ruleCollectionName` | string | The name of the parent rule collection. Required if the template is used in a standalone deployment. | +| `securityAdminConfigurationName` | string | The name of the parent security admin configuration. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `description` | string | `''` | A description of the rule. | +| `destinationPortRanges` | array | `[]` | List of destination port ranges. This specifies on which ports traffic will be allowed or denied by this rule. Provide an (*) to allow traffic on any port. Port ranges are between 1-65535. | +| `destinations` | array | `[]` | The destnations filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `sourcePortRanges` | array | `[]` | List of destination port ranges. This specifies on which ports traffic will be allowed or denied by this rule. Provide an (*) to allow traffic on any port. Port ranges are between 1-65535. | +| `sources` | array | `[]` | The source filter can be an IP Address or a service tag. Each filter contains the properties AddressPrefixType (IPPrefix or ServiceTag) and AddressPrefix (using CIDR notation (e.g. 192.168.99.0/24 or 2001:1234::/64) or a service tag (e.g. AppService.WestEurope)). Combining CIDR and Service tags in one rule filter is not permitted. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed rule. | +| `resourceGroupName` | string | The resource group the rule was deployed into. | +| `resourceId` | string | The resource ID of the deployed rule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/rules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/ruleCollections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/securityAdminConfigurations/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkManagers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..926fda216 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkSecurityGroup.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkSecurityGroup +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/dependencies.bicep new file mode 100644 index 000000000..a57e2a986 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/dependencies.bicep @@ -0,0 +1,24 @@ +@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 Application Security Group to create.') +param applicationSecurityGroupName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2022-01-01' = { + name: applicationSecurityGroupName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Application Security Group.') +output applicationSecurityGroupResourceId string = applicationSecurityGroup.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/deploy.test.bicep new file mode 100644 index 000000000..6595a27ab --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/common/deploy.test.bicep @@ -0,0 +1,148 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.networksecuritygroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nnsgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + applicationSecurityGroupName: 'dep-<>-asg-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + securityRules: [ + { + name: 'Specific' + properties: { + access: 'Allow' + description: 'Tests specific IPs and ports' + destinationAddressPrefix: '*' + destinationPortRange: '8080' + direction: 'Inbound' + priority: 100 + protocol: '*' + sourceAddressPrefix: '*' + sourcePortRange: '*' + } + } + { + name: 'Ranges' + properties: { + access: 'Allow' + description: 'Tests Ranges' + destinationAddressPrefixes: [ + '10.2.0.0/16' + '10.3.0.0/16' + ] + destinationPortRanges: [ + '90' + '91' + ] + direction: 'Inbound' + priority: 101 + protocol: '*' + sourceAddressPrefixes: [ + '10.0.0.0/16' + '10.1.0.0/16' + ] + sourcePortRanges: [ + '80' + '81' + ] + } + } + { + name: 'Port_8082' + properties: { + access: 'Allow' + description: 'Allow inbound access on TCP 8082' + destinationApplicationSecurityGroups: [ + { + id: nestedDependencies.outputs.applicationSecurityGroupResourceId + } + ] + destinationPortRange: '8082' + direction: 'Inbound' + priority: 102 + protocol: '*' + sourceApplicationSecurityGroups: [ + { + id: nestedDependencies.outputs.applicationSecurityGroupResourceId + } + ] + sourcePortRange: '*' + } + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/min/deploy.test.bicep new file mode 100644 index 000000000..398dd4f56 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.networksecuritygroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nnsgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/deploy.bicep new file mode 100644 index 000000000..85c19a5f2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/deploy.bicep @@ -0,0 +1,191 @@ +@description('Required. Name of the Network Security Group.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed.') +param securityRules array = [] + +@description('Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions.') +param flushConnection bool = false + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +// @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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the NSG resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'NetworkSecurityGroupEvent' + 'NetworkSecurityGroupRuleCounter' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var enableReferencedModulesTelemetry = false + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + /*retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + }*/ +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + /*retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + }*/ + } +] : diagnosticsLogsSpecified + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + flushConnection: flushConnection + securityRules: [for securityRule in securityRules: { + name: securityRule.name + properties: { + protocol: securityRule.properties.protocol + access: securityRule.properties.access + priority: securityRule.properties.priority + direction: securityRule.properties.direction + description: contains(securityRule.properties, 'description') ? securityRule.properties.description : '' + sourcePortRange: contains(securityRule.properties, 'sourcePortRange') ? securityRule.properties.sourcePortRange : '' + sourcePortRanges: contains(securityRule.properties, 'sourcePortRanges') ? securityRule.properties.sourcePortRanges : [] + destinationPortRange: contains(securityRule.properties, 'destinationPortRange') ? securityRule.properties.destinationPortRange : '' + destinationPortRanges: contains(securityRule.properties, 'destinationPortRanges') ? securityRule.properties.destinationPortRanges : [] + sourceAddressPrefix: contains(securityRule.properties, 'sourceAddressPrefix') ? securityRule.properties.sourceAddressPrefix : '' + destinationAddressPrefix: contains(securityRule.properties, 'destinationAddressPrefix') ? securityRule.properties.destinationAddressPrefix : '' + sourceAddressPrefixes: contains(securityRule.properties, 'sourceAddressPrefixes') ? securityRule.properties.sourceAddressPrefixes : [] + destinationAddressPrefixes: contains(securityRule.properties, 'destinationAddressPrefixes') ? securityRule.properties.destinationAddressPrefixes : [] + sourceApplicationSecurityGroups: contains(securityRule.properties, 'sourceApplicationSecurityGroups') ? securityRule.properties.sourceApplicationSecurityGroups : [] + destinationApplicationSecurityGroups: contains(securityRule.properties, 'destinationApplicationSecurityGroups') ? securityRule.properties.destinationApplicationSecurityGroups : [] + } + }] + } +} + +module networkSecurityGroup_securityRules 'securityRules/deploy.bicep' = [for (securityRule, index) in securityRules: { + name: '${uniqueString(deployment().name, location)}-securityRule-${index}' + params: { + name: securityRule.name + networkSecurityGroupName: networkSecurityGroup.name + protocol: securityRule.properties.protocol + access: securityRule.properties.access + priority: securityRule.properties.priority + direction: securityRule.properties.direction + description: contains(securityRule.properties, 'description') ? securityRule.properties.description : '' + sourcePortRange: contains(securityRule.properties, 'sourcePortRange') ? securityRule.properties.sourcePortRange : '' + sourcePortRanges: contains(securityRule.properties, 'sourcePortRanges') ? securityRule.properties.sourcePortRanges : [] + destinationPortRange: contains(securityRule.properties, 'destinationPortRange') ? securityRule.properties.destinationPortRange : '' + destinationPortRanges: contains(securityRule.properties, 'destinationPortRanges') ? securityRule.properties.destinationPortRanges : [] + sourceAddressPrefix: contains(securityRule.properties, 'sourceAddressPrefix') ? securityRule.properties.sourceAddressPrefix : '' + destinationAddressPrefix: contains(securityRule.properties, 'destinationAddressPrefix') ? securityRule.properties.destinationAddressPrefix : '' + sourceAddressPrefixes: contains(securityRule.properties, 'sourceAddressPrefixes') ? securityRule.properties.sourceAddressPrefixes : [] + destinationAddressPrefixes: contains(securityRule.properties, 'destinationAddressPrefixes') ? securityRule.properties.destinationAddressPrefixes : [] + sourceApplicationSecurityGroups: contains(securityRule.properties, 'sourceApplicationSecurityGroups') ? securityRule.properties.sourceApplicationSecurityGroups : [] + destinationApplicationSecurityGroups: contains(securityRule.properties, 'destinationApplicationSecurityGroups') ? securityRule.properties.destinationApplicationSecurityGroups : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource networkSecurityGroup_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${networkSecurityGroup.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkSecurityGroup +} + +resource networkSecurityGroup_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + logs: diagnosticsLogs + } + scope: networkSecurityGroup +} + +module networkSecurityGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NSG-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkSecurityGroup.id + } +}] + +@description('The resource group the network security group was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the network security group.') +output resourceId string = networkSecurityGroup.id + +@description('The name of the network security group.') +output name string = networkSecurityGroup.name + +@description('The location the resource was deployed into.') +output location string = networkSecurityGroup.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/readme.md new file mode 100644 index 000000000..509b73dd7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/readme.md @@ -0,0 +1,443 @@ +# Network Security Groups `[Microsoft.Network/networkSecurityGroups]` + +This template deploys a network security group (NSG) with optional security rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/networkSecurityGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkSecurityGroups) | +| `Microsoft.Network/networkSecurityGroups/securityRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkSecurityGroups/securityRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Network Security Group. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, NetworkSecurityGroupEvent, NetworkSecurityGroupRuleCounter]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `flushConnection` | bool | `False` | | When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `securityRules` | _[securityRules](securityRules/readme.md)_ array | `[]` | | Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed. | +| `tags` | object | `{object}` | | Tags of the NSG resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the network security group. | +| `resourceGroupName` | string | The resource group the network security group was deployed into. | +| `resourceId` | string | The resource ID of the network security group. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module networkSecurityGroups './Microsoft.Network/networkSecurityGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnsgcom' + params: { + // Required parameters + name: '<>nnsgcom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + securityRules: [ + { + name: 'Specific' + properties: { + access: 'Allow' + description: 'Tests specific IPs and ports' + destinationAddressPrefix: '*' + destinationPortRange: '8080' + direction: 'Inbound' + priority: 100 + protocol: '*' + sourceAddressPrefix: '*' + sourcePortRange: '*' + } + } + { + name: 'Ranges' + properties: { + access: 'Allow' + description: 'Tests Ranges' + destinationAddressPrefixes: [ + '10.2.0.0/16' + '10.3.0.0/16' + ] + destinationPortRanges: [ + '90' + '91' + ] + direction: 'Inbound' + priority: 101 + protocol: '*' + sourceAddressPrefixes: [ + '10.0.0.0/16' + '10.1.0.0/16' + ] + sourcePortRanges: [ + '80' + '81' + ] + } + } + { + name: 'Port_8082' + properties: { + access: 'Allow' + description: 'Allow inbound access on TCP 8082' + destinationApplicationSecurityGroups: [ + { + id: '' + } + ] + destinationPortRange: '8082' + direction: 'Inbound' + priority: 102 + protocol: '*' + sourceApplicationSecurityGroups: [ + { + id: '' + } + ] + sourcePortRange: '*' + } + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nnsgcom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "securityRules": { + "value": [ + { + "name": "Specific", + "properties": { + "access": "Allow", + "description": "Tests specific IPs and ports", + "destinationAddressPrefix": "*", + "destinationPortRange": "8080", + "direction": "Inbound", + "priority": 100, + "protocol": "*", + "sourceAddressPrefix": "*", + "sourcePortRange": "*" + } + }, + { + "name": "Ranges", + "properties": { + "access": "Allow", + "description": "Tests Ranges", + "destinationAddressPrefixes": [ + "10.2.0.0/16", + "10.3.0.0/16" + ], + "destinationPortRanges": [ + "90", + "91" + ], + "direction": "Inbound", + "priority": 101, + "protocol": "*", + "sourceAddressPrefixes": [ + "10.0.0.0/16", + "10.1.0.0/16" + ], + "sourcePortRanges": [ + "80", + "81" + ] + } + }, + { + "name": "Port_8082", + "properties": { + "access": "Allow", + "description": "Allow inbound access on TCP 8082", + "destinationApplicationSecurityGroups": [ + { + "id": "" + } + ], + "destinationPortRange": "8082", + "direction": "Inbound", + "priority": 102, + "protocol": "*", + "sourceApplicationSecurityGroups": [ + { + "id": "" + } + ], + "sourcePortRange": "*" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module networkSecurityGroups './Microsoft.Network/networkSecurityGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnsgmin' + params: { + // Required parameters + name: '<>nnsgmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nnsgmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep new file mode 100644 index 000000000..5fc3c9523 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/deploy.bicep @@ -0,0 +1,117 @@ +@sys.description('Required. The name of the security rule.') +param name string + +@sys.description('Conditional. The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment.') +param networkSecurityGroupName string + +@sys.description('Optional. Whether network traffic is allowed or denied.') +@allowed([ + 'Allow' + 'Deny' +]) +param access string = 'Deny' + +@sys.description('Optional. A description for this rule.') +@maxLength(140) +param description string = '' + +@sys.description('Optional. The destination address prefix. CIDR or destination IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used.') +param destinationAddressPrefix string = '' + +@sys.description('Optional. The destination address prefixes. CIDR or destination IP ranges.') +param destinationAddressPrefixes array = [] + +@sys.description('Optional. The application security group specified as destination.') +param destinationApplicationSecurityGroups array = [] + +@sys.description('Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports.') +param destinationPortRange string = '' + +@sys.description('Optional. The destination port ranges.') +param destinationPortRanges array = [] + +@sys.description('Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic.') +@allowed([ + 'Inbound' + 'Outbound' +]) +param direction string + +@sys.description('Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule.') +param priority int + +@sys.description('Required. Network protocol this rule applies to.') +@allowed([ + '*' + 'Ah' + 'Esp' + 'Icmp' + 'Tcp' + 'Udp' +]) +param protocol string + +@sys.description('Optional. The CIDR or source IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used. If this is an ingress rule, specifies where network traffic originates from.') +param sourceAddressPrefix string = '' + +@sys.description('Optional. The CIDR or source IP ranges.') +param sourceAddressPrefixes array = [] + +@sys.description('Optional. The application security group specified as source.') +param sourceApplicationSecurityGroups array = [] + +@sys.description('Optional. The source port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports.') +param sourcePortRange string = '' + +@sys.description('Optional. The source port ranges.') +param sourcePortRanges array = [] + +@sys.description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-07-01' existing = { + name: networkSecurityGroupName +} + +resource securityRule 'Microsoft.Network/networkSecurityGroups/securityRules@2022-07-01' = { + name: name + parent: networkSecurityGroup + properties: { + access: access + description: description + destinationAddressPrefix: destinationAddressPrefix + destinationAddressPrefixes: destinationAddressPrefixes + destinationApplicationSecurityGroups: destinationApplicationSecurityGroups + destinationPortRange: destinationPortRange + destinationPortRanges: destinationPortRanges + direction: direction + priority: priority + protocol: protocol + sourceAddressPrefix: sourceAddressPrefix + sourceAddressPrefixes: sourceAddressPrefixes + sourceApplicationSecurityGroups: sourceApplicationSecurityGroups + sourcePortRange: sourcePortRange + sourcePortRanges: sourcePortRanges + } +} + +@sys.description('The resource group the security rule was deployed into.') +output resourceGroupName string = resourceGroup().name + +@sys.description('The resource ID of the security rule.') +output resourceId string = securityRule.id + +@sys.description('The name of the security rule.') +output name string = securityRule.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/readme.md new file mode 100644 index 000000000..1d4583c0f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/readme.md @@ -0,0 +1,64 @@ +# Network Security Groups Security Rules `[Microsoft.Network/networkSecurityGroups/securityRules]` + +This module deploys Network Security Group Security Rules. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkSecurityGroups/securityRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkSecurityGroups/securityRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `direction` | string | `[Inbound, Outbound]` | The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic. | +| `name` | string | | The name of the security rule. | +| `priority` | int | | The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule. | +| `protocol` | string | `[*, Ah, Esp, Icmp, Tcp, Udp]` | Network protocol this rule applies to. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `networkSecurityGroupName` | string | The name of the parent network security group to deploy the security rule into. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `access` | string | `'Deny'` | `[Allow, Deny]` | Whether network traffic is allowed or denied. | +| `description` | string | `''` | | A description for this rule. | +| `destinationAddressPrefix` | string | `''` | | The destination address prefix. CIDR or destination IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used. | +| `destinationAddressPrefixes` | array | `[]` | | The destination address prefixes. CIDR or destination IP ranges. | +| `destinationApplicationSecurityGroups` | array | `[]` | | The application security group specified as destination. | +| `destinationPortRange` | string | `''` | | The destination port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports. | +| `destinationPortRanges` | array | `[]` | | The destination port ranges. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `sourceAddressPrefix` | string | `''` | | The CIDR or source IP range. Asterisk "*" can also be used to match all source IPs. Default tags such as "VirtualNetwork", "AzureLoadBalancer" and "Internet" can also be used. If this is an ingress rule, specifies where network traffic originates from. | +| `sourceAddressPrefixes` | array | `[]` | | The CIDR or source IP ranges. | +| `sourceApplicationSecurityGroups` | array | `[]` | | The application security group specified as source. | +| `sourcePortRange` | string | `''` | | The source port or range. Integer or range between 0 and 65535. Asterisk "*" can also be used to match all ports. | +| `sourcePortRanges` | array | `[]` | | The source port ranges. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the security rule. | +| `resourceGroupName` | string | The resource group the security rule was deployed into. | +| `resourceId` | string | The resource ID of the security rule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/securityRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkSecurityGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..4d8318611 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(networkWatcher.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: networkWatcher +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/dependencies.bicep new file mode 100644 index 000000000..e9c49b8dd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/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@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource firstNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-05-01' = { + name: firstNetworkSecurityGroupName + location: location +} + +resource secondNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-05-01' = { + name: secondNetworkSecurityGroupName + location: location +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2022-05-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/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/deploy.test.bicep new file mode 100644 index 000000000..51b3ba298 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/common/deploy.test.bicep @@ -0,0 +1,153 @@ +targetScope = 'subscription' + +// ========== // +// 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.') +param location 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 = 'nnwcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + firstNetworkSecurityGroupName: 'dep-<>-nsg-1-${serviceShort}' + secondNetworkSecurityGroupName: 'dep-<>-nsg-2-${serviceShort}' + virtualMachineName: 'dep-<>-vm-${serviceShort}' + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + location: location + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // +#disable-next-line no-hardcoded-location // Disabled as the default RG & location are created in always one location, but each test has to deploy into a different one +var testLocation = 'westeurope' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: 'NetworkWatcher_${testLocation}' + location: testLocation + connectionMonitors: [ + { + name: '<>-${serviceShort}-cm-001' + endpoints: [ + { + name: '<>-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: [ + '<>-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: '<>-${serviceShort}-fl-001' + retentionInDays: 8 + storageId: diagnosticDependencies.outputs.storageAccountResourceId + targetResourceId: nestedDependencies.outputs.secondNetworkSecurityGroupResourceId + trafficAnalyticsInterval: 10 + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/min/deploy.test.bicep new file mode 100644 index 000000000..52f2bdc20 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/.test/min/deploy.test.bicep @@ -0,0 +1,44 @@ +targetScope = 'subscription' + +// ========== // +// 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.') +param location 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 = 'nnwmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // +#disable-next-line no-hardcoded-location // Disabled as the default RG & location are created in always one location, but each test has to deploy into a different one +var testLocation = 'northeurope' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + // Note: This value is not required and only set to enable testing + location: testLocation + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep new file mode 100644 index 000000000..8c972ea68 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/deploy.bicep @@ -0,0 +1,76 @@ +@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 = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var outputs = !empty(workspaceResourceId) ? [ + { + type: 'Workspace' + workspaceSettings: { + workspaceResourceId: workspaceResourceId + } + } +] : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2022-07-01' existing = { + name: networkWatcherName +} + +resource connectionMonitor 'Microsoft.Network/networkWatchers/connectionMonitors@2022-07-01' = { + name: name + parent: networkWatcher + tags: tags + location: location + properties: { + endpoints: endpoints + testConfigurations: testConfigurations + testGroups: testGroups + outputs: outputs + } +} + +@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/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/readme.md new file mode 100644 index 000000000..59cb0ab72 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/readme.md @@ -0,0 +1,92 @@ +# Network Watchers Connection Monitors `[Microsoft.Network/networkWatchers/connectionMonitors]` + +This template deploys Connection Monitors. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkWatchers/connectionMonitors` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkWatchers/connectionMonitors) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the resource. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `endpoints` | array | `[]` | List of connection monitor endpoints. | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `networkWatcherName` | string | `[format('NetworkWatcher_{0}', resourceGroup().location)]` | 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` | object | `{object}` | Tags of the resource. | +| `testConfigurations` | array | `[]` | List of connection monitor test configurations. | +| `testGroups` | array | `[]` | List of connection monitor test groups. | +| `workspaceResourceId` | string | `''` | Specify the Log Analytics Workspace Resource ID. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | 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_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/connectionMonitors/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/deploy.bicep new file mode 100644 index 000000000..0f8b8e9d8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/deploy.bicep @@ -0,0 +1,114 @@ +@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 = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2022-07-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource networkWatcher_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${networkWatcher.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: networkWatcher +} + +module networkWatcher_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-NW-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: networkWatcher.id + } +}] + +module networkWatcher_connectionMonitors 'connectionMonitors/deploy.bicep' = [for (connectionMonitor, index) in connectionMonitors: { + name: '${uniqueString(deployment().name, location)}-NW-ConnectionMonitor-${index}' + params: { + endpoints: contains(connectionMonitor, 'endpoints') ? connectionMonitor.endpoints : [] + name: connectionMonitor.name + networkWatcherName: networkWatcher.name + testConfigurations: contains(connectionMonitor, 'testConfigurations') ? connectionMonitor.testConfigurations : [] + testGroups: contains(connectionMonitor, 'testGroups') ? connectionMonitor.testGroups : [] + workspaceResourceId: contains(connectionMonitor, 'workspaceResourceId') ? connectionMonitor.workspaceResourceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module networkWatcher_flowLogs 'flowLogs/deploy.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 : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@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 diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep new file mode 100644 index 000000000..5cc24080d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/deploy.bicep @@ -0,0 +1,105 @@ +@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 + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var flowAnalyticsConfiguration = !empty(workspaceResourceId) && enabled == true ? { + networkWatcherFlowAnalyticsConfiguration: { + enabled: true + workspaceResourceId: workspaceResourceId + trafficAnalyticsInterval: trafficAnalyticsInterval + } +} : { + networkWatcherFlowAnalyticsConfiguration: { + enabled: false + } +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource networkWatcher 'Microsoft.Network/networkWatchers@2022-07-01' existing = { + name: networkWatcherName +} + +resource flowLog 'Microsoft.Network/networkWatchers/flowLogs@2022-07-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/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/readme.md new file mode 100644 index 000000000..3b7944129 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/readme.md @@ -0,0 +1,96 @@ +# 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) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/networkWatchers/flowLogs` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkWatchers/flowLogs) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageId` | string | Resource ID of the diagnostic storage account. | +| `targetResourceId` | string | Resource ID of the NSG that must be enabled for Flow Logs. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enabled` | bool | `True` | | If the flow log should be enabled. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `formatVersion` | int | `2` | `[1, 2]` | The flow log format version. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `name` | string | `[format('{0}-{1}-flowlog', last(split(parameters('targetResourceId'), '/')), split(parameters('targetResourceId'), '/')[4])]` | | Name of the resource. | +| `networkWatcherName` | string | `[format('NetworkWatcher_{0}', resourceGroup().location)]` | | 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` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `trafficAnalyticsInterval` | int | `60` | `[10, 60]` | The interval in minutes which would decide how frequently TA service should do flow analytics. | +| `workspaceResourceId` | string | `''` | | Specify the Log Analytics Workspace Resource ID. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | 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_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/flowLogs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/readme.md new file mode 100644 index 000000000..a6ecd2681 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/readme.md @@ -0,0 +1,416 @@ +# Network Watchers `[Microsoft.Network/networkWatchers]` + +- This template deploys a network watcher. +- Network Watcher is a default resource which will get created automatically in every region where a virtual network is present with in the network watcher resource group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkWatchers) | +| `Microsoft.Network/networkWatchers/connectionMonitors` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkWatchers/connectionMonitors) | +| `Microsoft.Network/networkWatchers/flowLogs` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/networkWatchers/flowLogs) | + +## Parameters + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `connectionMonitors` | _[connectionMonitors](connectionMonitors/readme.md)_ array | `[]` | | Array that contains the Connection Monitors. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `flowLogs` | _[flowLogs](flowLogs/readme.md)_ array | `[]` | | Array that contains the Flow Logs. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `name` | string | `[format('NetworkWatcher_{0}', parameters('location'))]` | | Name of the Network Watcher resource (hidden). | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | 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_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module networkWatchers './Microsoft.Network/networkWatchers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnwcom' + params: { + connectionMonitors: [ + { + endpoints: [ + { + name: '' + resourceId: '' + type: 'AzureVM' + } + { + address: 'www.bing.com' + name: 'Bing' + type: 'ExternalAddress' + } + ] + name: '<>-nnwcom-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: '' + } + ] + enableDefaultTelemetry: '' + flowLogs: [ + { + enabled: false + storageId: '' + targetResourceId: '' + } + { + formatVersion: 1 + name: '<>-nnwcom-fl-001' + retentionInDays: 8 + storageId: '' + targetResourceId: '' + trafficAnalyticsInterval: 10 + workspaceResourceId: '' + } + ] + location: '' + name: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>-nnwcom-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": "" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "flowLogs": { + "value": [ + { + "enabled": false, + "storageId": "", + "targetResourceId": "" + }, + { + "formatVersion": 1, + "name": "<>-nnwcom-fl-001", + "retentionInDays": 8, + "storageId": "", + "targetResourceId": "", + "trafficAnalyticsInterval": 10, + "workspaceResourceId": "" + } + ] + }, + "location": { + "value": "" + }, + "name": { + "value": "" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module networkWatchers './Microsoft.Network/networkWatchers/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nnwmin' + params: { + enableDefaultTelemetry: '' + location: '' + } +} +``` + +
+

+ +

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

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/networkWatchers/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..8c8f01389 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2018-09-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateDnsZone.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateDnsZone +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/dependencies.bicep new file mode 100644 index 000000000..581b2cd44 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/dependencies.bicep @@ -0,0 +1,41 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/deploy.test.bicep new file mode 100644 index 000000000..cf985509a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/common/deploy.test.bicep @@ -0,0 +1,230 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privatednszones-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npdzcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001.com' + a: [ + { + aRecords: [ + { + ipv4Address: '10.240.4.4' + } + ] + name: 'A_10.240.4.4' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + ttl: 3600 + } + ] + aaaa: [ + { + aaaaRecords: [ + { + ipv6Address: '2001:0db8:85a3:0000:0000:8a2e:0370:7334' + } + ] + name: 'AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334' + ttl: 3600 + } + ] + cname: [ + { + cnameRecord: { + cname: 'test' + } + name: 'CNAME_test' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + ttl: 3600 + } + ] + lock: 'CanNotDelete' + mx: [ + { + mxRecords: [ + { + exchange: 'contoso.com' + preference: 100 + } + ] + name: 'MX_contoso' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + ttl: 3600 + } + ] + ptr: [ + { + name: 'PTR_contoso' + ptrRecords: [ + { + ptrdname: 'contoso.com' + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + ttl: 3600 + } + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + soa: [ + { + name: '@' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + soaRecord: { + email: 'azureprivatedns-host.microsoft.com' + expireTime: 2419200 + host: 'azureprivatedns.net' + minimumTtl: 10 + refreshTime: 3600 + retryTime: 300 + serialNumber: '1' + } + ttl: 3600 + } + ] + srv: [ + { + name: 'SRV_contoso' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + srvRecords: [ + { + port: 9332 + priority: 0 + target: 'test.contoso.com' + weight: 0 + } + ] + ttl: 3600 + } + ] + txt: [ + { + name: 'TXT_test' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + ttl: 3600 + txtRecords: [ + { + value: [ + 'test' + ] + } + ] + } + ] + virtualNetworkLinks: [ + { + registrationEnabled: true + virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/min/deploy.test.bicep new file mode 100644 index 000000000..2c20614c7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privatednszones-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npdzmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001.com' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..65ecd6c84 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource A 'Microsoft.Network/privateDnsZones/A@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(A.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: A +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/deploy.bicep new file mode 100644 index 000000000..9614af880 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the A record.') +param name string + +@description('Optional. The list of A records in the record set.') +param aRecords array = [] + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource A 'Microsoft.Network/privateDnsZones/A@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + aRecords: aRecords + metadata: metadata + ttl: ttl + } +} + +module A_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSA-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: A.id + } +}] + +@description('The name of the deployed A record.') +output name string = A.name + +@description('The resource ID of the deployed A record.') +output resourceId string = A.id + +@description('The resource group of the deployed A record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/readme.md new file mode 100644 index 000000000..01c453878 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone A record `[Microsoft.Network/privateDnsZones/A]` + +This module deploys a Private DNS Zone A record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/A` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/A) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the A record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `aRecords` | array | `[]` | The list of A records in the record set. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed A record. | +| `resourceGroupName` | string | The resource group of the deployed A record. | +| `resourceId` | string | The resource ID of the deployed A record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/A/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..d36e17cd5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource AAAA 'Microsoft.Network/privateDnsZones/AAAA@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(AAAA.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: AAAA +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep new file mode 100644 index 000000000..a8a64139c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the AAAA record.') +param name string + +@description('Optional. The list of AAAA records in the record set.') +param aaaaRecords array = [] + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource AAAA 'Microsoft.Network/privateDnsZones/AAAA@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + aaaaRecords: aaaaRecords + metadata: metadata + ttl: ttl + } +} + +module AAAA_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSAAAA-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: AAAA.id + } +}] + +@description('The name of the deployed AAAA record.') +output name string = AAAA.name + +@description('The resource ID of the deployed AAAA record.') +output resourceId string = AAAA.id + +@description('The resource group of the deployed AAAA record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/readme.md new file mode 100644 index 000000000..05822f584 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone AAAA record `[Microsoft.Network/privateDnsZones/AAAA]` + +This module deploys a Private DNS Zone AAAA record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/AAAA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/AAAA) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the AAAA record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `aaaaRecords` | array | `[]` | The list of AAAA records in the record set. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed AAAA record. | +| `resourceGroupName` | string | The resource group of the deployed AAAA record. | +| `resourceId` | string | The resource ID of the deployed AAAA record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/AAAA/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..ee869eadd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,99 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource CNAME 'Microsoft.Network/privateDnsZones/CNAME@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(CNAME.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: CNAME +}] + +output id string = roleAssignment[0].name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep new file mode 100644 index 000000000..9c1b32f95 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the CNAME record.') +param name string + +@description('Optional. A CNAME record.') +param cnameRecord object = {} + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource CNAME 'Microsoft.Network/privateDnsZones/CNAME@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + cnameRecord: cnameRecord + metadata: metadata + ttl: ttl + } +} + +module CNAME_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSCNAME-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: CNAME.id + } +}] + +@description('The name of the deployed CNAME record.') +output name string = CNAME.name + +@description('The resource ID of the deployed CNAME record.') +output resourceId string = CNAME.id + +@description('The resource group of the deployed CNAME record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/readme.md new file mode 100644 index 000000000..314938dbe --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone CNAME record `[Microsoft.Network/privateDnsZones/CNAME]` + +This module deploys a Private DNS Zone CNAME record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/CNAME` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/CNAME) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the CNAME record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `cnameRecord` | object | `{object}` | A CNAME record. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed CNAME record. | +| `resourceGroupName` | string | The resource group of the deployed CNAME record. | +| `resourceId` | string | The resource ID of the deployed CNAME record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/CNAME/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..809a04c7b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource MX 'Microsoft.Network/privateDnsZones/MX@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(MX.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: MX +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/deploy.bicep new file mode 100644 index 000000000..b164def40 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the MX record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The list of MX records in the record set.') +param mxRecords array = [] + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource MX 'Microsoft.Network/privateDnsZones/MX@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + mxRecords: mxRecords + ttl: ttl + } +} + +module MX_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSMX-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: MX.id + } +}] + +@description('The name of the deployed MX record.') +output name string = MX.name + +@description('The resource ID of the deployed MX record.') +output resourceId string = MX.id + +@description('The resource group of the deployed MX record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/readme.md new file mode 100644 index 000000000..b18d99bca --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone MX record `[Microsoft.Network/privateDnsZones/MX]` + +This module deploys a Private DNS Zone MX record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/MX` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/MX) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the MX record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `mxRecords` | array | `[]` | The list of MX records in the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed MX record. | +| `resourceGroupName` | string | The resource group of the deployed MX record. | +| `resourceId` | string | The resource ID of the deployed MX record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/MX/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..792d01b6c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource PTR 'Microsoft.Network/privateDnsZones/PTR@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(PTR.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: PTR +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/deploy.bicep new file mode 100644 index 000000000..f61d908bd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the PTR record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The list of PTR records in the record set.') +param ptrRecords array = [] + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module PTR_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSPTR-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: PTR.id + } +}] + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource PTR 'Microsoft.Network/privateDnsZones/PTR@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + ptrRecords: ptrRecords + ttl: ttl + } +} + +@description('The name of the deployed PTR record.') +output name string = PTR.name + +@description('The resource ID of the deployed PTR record.') +output resourceId string = PTR.id + +@description('The resource group of the deployed PTR record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/readme.md new file mode 100644 index 000000000..76398f192 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone PTR record `[Microsoft.Network/privateDnsZones/PTR]` + +This module deploys a Private DNS Zone PTR record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/PTR` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/PTR) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the PTR record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `ptrRecords` | array | `[]` | The list of PTR records in the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed PTR record. | +| `resourceGroupName` | string | The resource group of the deployed PTR record. | +| `resourceId` | string | The resource ID of the deployed PTR record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/PTR/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..d24a71ffc --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource SOA 'Microsoft.Network/privateDnsZones/SOA@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(SOA.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: SOA +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/deploy.bicep new file mode 100644 index 000000000..23f3e22ae --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the SOA record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. A SOA record.') +param soaRecord object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource SOA 'Microsoft.Network/privateDnsZones/SOA@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + soaRecord: soaRecord + ttl: ttl + } +} + +module SOA_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSSOA-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: SOA.id + } +}] + +@description('The name of the deployed SOA record.') +output name string = SOA.name + +@description('The resource ID of the deployed SOA record.') +output resourceId string = SOA.id + +@description('The resource group of the deployed SOA record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/readme.md new file mode 100644 index 000000000..7f92d5124 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone SOA record `[Microsoft.Network/privateDnsZones/SOA]` + +This module deploys a Private DNS Zone SOA record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/SOA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SOA) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the SOA record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `soaRecord` | object | `{object}` | A SOA record. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed SOA record. | +| `resourceGroupName` | string | The resource group of the deployed SOA record. | +| `resourceId` | string | The resource ID of the deployed SOA record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SOA/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..8237ff317 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource SRV 'Microsoft.Network/privateDnsZones/SRV@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(SRV.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: SRV +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/deploy.bicep new file mode 100644 index 000000000..4b07a738c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the SRV record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The list of SRV records in the record set.') +param srvRecords array = [] + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource SRV 'Microsoft.Network/privateDnsZones/SRV@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + srvRecords: srvRecords + ttl: ttl + } +} + +module SRV_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSSRV-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: SRV.id + } +}] + +@description('The name of the deployed SRV record.') +output name string = SRV.name + +@description('The resource ID of the deployed SRV record.') +output resourceId string = SRV.id + +@description('The resource group of the deployed SRV record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/readme.md new file mode 100644 index 000000000..365041951 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/readme.md @@ -0,0 +1,113 @@ +# Private DNS Zone SRV record `[Microsoft.Network/privateDnsZones/SRV]` + +This module deploys a Private DNS Zone TXT record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/SRV` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SRV) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the SRV record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `srvRecords` | array | `[]` | The list of SRV records in the record set. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed SRV record. | +| `resourceGroupName` | string | The resource group of the deployed SRV record. | +| `resourceId` | string | The resource ID of the deployed SRV record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/SRV/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..740847658 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource TXT 'Microsoft.Network/privateDnsZones/TXT@2018-09-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(TXT.name, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: TXT +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/deploy.bicep new file mode 100644 index 000000000..11fda35b5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/deploy.bicep @@ -0,0 +1,68 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Required. The name of the TXT record.') +param name string + +@description('Optional. The metadata attached to the record set.') +param metadata object = {} + +@description('Optional. The TTL (time-to-live) of the records in the record set.') +param ttl int = 3600 + +@description('Optional. The list of TXT records in the record set.') +param txtRecords array = [] + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource TXT 'Microsoft.Network/privateDnsZones/TXT@2020-06-01' = { + name: name + parent: privateDnsZone + properties: { + metadata: metadata + ttl: ttl + txtRecords: txtRecords + } +} + +module TXT_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-PDNSTXT-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: TXT.id + } +}] + +@description('The name of the deployed TXT record.') +output name string = TXT.name + +@description('The resource ID of the deployed TXT record.') +output resourceId string = TXT.id + +@description('The resource group of the deployed TXT record.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/readme.md new file mode 100644 index 000000000..55635b80c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/readme.md @@ -0,0 +1,146 @@ +# Private DNS Zone TXT record `[Microsoft.Network/privateDnsZones/TXT]` + +This module deploys a Private DNS Zone TXT record. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/privateDnsZones/TXT` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/TXT) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the TXT record. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `metadata` | object | `{object}` | The metadata attached to the record set. | +| `roleAssignments` | array | `[]` | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `ttl` | int | `3600` | The TTL (time-to-live) of the records in the record set. | +| `txtRecords` | array | `[]` | The list of TXT records in the record set. | + + +### Parameter Usage: `txtRecords` + +

+ +Parameter JSON format + +```json +"txtRecords": { + "value": [ + { + "value": [ "string" ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +txtRecords: [ + { + value: [ 'string' ] + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed TXT record. | +| `resourceGroupName` | string | The resource group of the deployed TXT record. | +| `resourceId` | string | The resource ID of the deployed TXT record. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/TXT/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/deploy.bicep new file mode 100644 index 000000000..eb6c21e79 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/deploy.bicep @@ -0,0 +1,220 @@ +@description('Required. Private DNS zone name.') +param name string + +@description('Optional. Array of A records.') +param a array = [] + +@description('Optional. Array of AAAA records.') +param aaaa array = [] + +@description('Optional. Array of CNAME records.') +param cname array = [] + +@description('Optional. Array of MX records.') +param mx array = [] + +@description('Optional. Array of PTR records.') +param ptr array = [] + +@description('Optional. Array of SOA records.') +param soa array = [] + +@description('Optional. Array of SRV records.') +param srv array = [] + +@description('Optional. Array of TXT records.') +param txt array = [] + +@description('Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties \'vnetResourceId\' and \'registrationEnabled\'. The \'vnetResourceId\' is a resource ID of a vNet to link, \'registrationEnabled\' (bool) enables automatic DNS registration in the zone for the linked vNet.') +param virtualNetworkLinks array = [] + +@description('Optional. The location of the PrivateDNSZone. Should be global.') +param location string = 'global' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: name + location: location + tags: tags +} + +module privateDnsZone_A 'A/deploy.bicep' = [for (aRecord, index) in a: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-ARecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: aRecord.name + aRecords: contains(aRecord, 'aRecords') ? aRecord.aRecords : [] + metadata: contains(aRecord, 'metadata') ? aRecord.metadata : {} + ttl: contains(aRecord, 'ttl') ? aRecord.ttl : 3600 + roleAssignments: contains(aRecord, 'roleAssignments') ? aRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_AAAA 'AAAA/deploy.bicep' = [for (aaaaRecord, index) in aaaa: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-AAAARecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: aaaaRecord.name + aaaaRecords: contains(aaaaRecord, 'aaaaRecords') ? aaaaRecord.aaaaRecords : [] + metadata: contains(aaaaRecord, 'metadata') ? aaaaRecord.metadata : {} + ttl: contains(aaaaRecord, 'ttl') ? aaaaRecord.ttl : 3600 + roleAssignments: contains(aaaaRecord, 'roleAssignments') ? aaaaRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_CNAME 'CNAME/deploy.bicep' = [for (cnameRecord, index) in cname: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-CNAMERecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: cnameRecord.name + cnameRecord: contains(cnameRecord, 'cnameRecord') ? cnameRecord.cnameRecord : {} + metadata: contains(cnameRecord, 'metadata') ? cnameRecord.metadata : {} + ttl: contains(cnameRecord, 'ttl') ? cnameRecord.ttl : 3600 + roleAssignments: contains(cnameRecord, 'roleAssignments') ? cnameRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_MX 'MX/deploy.bicep' = [for (mxRecord, index) in mx: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-MXRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: mxRecord.name + metadata: contains(mxRecord, 'metadata') ? mxRecord.metadata : {} + mxRecords: contains(mxRecord, 'mxRecords') ? mxRecord.mxRecords : [] + ttl: contains(mxRecord, 'ttl') ? mxRecord.ttl : 3600 + roleAssignments: contains(mxRecord, 'roleAssignments') ? mxRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_PTR 'PTR/deploy.bicep' = [for (ptrRecord, index) in ptr: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-PTRRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: ptrRecord.name + metadata: contains(ptrRecord, 'metadata') ? ptrRecord.metadata : {} + ptrRecords: contains(ptrRecord, 'ptrRecords') ? ptrRecord.ptrRecords : [] + ttl: contains(ptrRecord, 'ttl') ? ptrRecord.ttl : 3600 + roleAssignments: contains(ptrRecord, 'roleAssignments') ? ptrRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_SOA 'SOA/deploy.bicep' = [for (soaRecord, index) in soa: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-SOARecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: soaRecord.name + metadata: contains(soaRecord, 'metadata') ? soaRecord.metadata : {} + soaRecord: contains(soaRecord, 'soaRecord') ? soaRecord.soaRecord : {} + ttl: contains(soaRecord, 'ttl') ? soaRecord.ttl : 3600 + roleAssignments: contains(soaRecord, 'roleAssignments') ? soaRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_SRV 'SRV/deploy.bicep' = [for (srvRecord, index) in srv: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-SRVRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: srvRecord.name + metadata: contains(srvRecord, 'metadata') ? srvRecord.metadata : {} + srvRecords: contains(srvRecord, 'srvRecords') ? srvRecord.srvRecords : [] + ttl: contains(srvRecord, 'ttl') ? srvRecord.ttl : 3600 + roleAssignments: contains(srvRecord, 'roleAssignments') ? srvRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_TXT 'TXT/deploy.bicep' = [for (txtRecord, index) in txt: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-TXTRecord-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: txtRecord.name + metadata: contains(txtRecord, 'metadata') ? txtRecord.metadata : {} + txtRecords: contains(txtRecord, 'txtRecords') ? txtRecord.txtRecords : [] + ttl: contains(txtRecord, 'ttl') ? txtRecord.ttl : 3600 + roleAssignments: contains(txtRecord, 'roleAssignments') ? txtRecord.roleAssignments : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module privateDnsZone_virtualNetworkLinks 'virtualNetworkLinks/deploy.bicep' = [for (virtualNetworkLink, index) in virtualNetworkLinks: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-VirtualNetworkLink-${index}' + params: { + privateDnsZoneName: privateDnsZone.name + name: contains(virtualNetworkLink, 'name') ? virtualNetworkLink.name : '${last(split(virtualNetworkLink.virtualNetworkResourceId, '/'))}-vnetlink' + virtualNetworkResourceId: virtualNetworkLink.virtualNetworkResourceId + location: contains(virtualNetworkLink, 'location') ? virtualNetworkLink.location : 'global' + registrationEnabled: contains(virtualNetworkLink, 'registrationEnabled') ? virtualNetworkLink.registrationEnabled : false + tags: contains(virtualNetworkLink, 'tags') ? virtualNetworkLink.tags : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource privateDnsZone_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${privateDnsZone.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateDnsZone +} + +module privateDnsZone_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PrivateDnsZone-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateDnsZone.id + } +}] + +@description('The resource group the private DNS zone was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the private DNS zone.') +output name string = privateDnsZone.name + +@description('The resource ID of the private DNS zone.') +output resourceId string = privateDnsZone.id + +@description('The location the resource was deployed into.') +output location string = privateDnsZone.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/readme.md new file mode 100644 index 000000000..79c3f14eb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/readme.md @@ -0,0 +1,648 @@ +# Private DNS Zones `[Microsoft.Network/privateDnsZones]` + +This template deploys a private DNS zone. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/privateDnsZones` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones) | +| `Microsoft.Network/privateDnsZones/A` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/A) | +| `Microsoft.Network/privateDnsZones/AAAA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/AAAA) | +| `Microsoft.Network/privateDnsZones/CNAME` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/CNAME) | +| `Microsoft.Network/privateDnsZones/MX` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/MX) | +| `Microsoft.Network/privateDnsZones/PTR` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/PTR) | +| `Microsoft.Network/privateDnsZones/SOA` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SOA) | +| `Microsoft.Network/privateDnsZones/SRV` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/SRV) | +| `Microsoft.Network/privateDnsZones/TXT` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/TXT) | +| `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Private DNS zone name. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `a` | _[A](A/readme.md)_ array | `[]` | | Array of A records. | +| `aaaa` | _[AAAA](AAAA/readme.md)_ array | `[]` | | Array of AAAA records. | +| `cname` | _[CNAME](CNAME/readme.md)_ array | `[]` | | Array of CNAME records. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `'global'` | | The location of the PrivateDNSZone. Should be global. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `mx` | _[MX](MX/readme.md)_ array | `[]` | | Array of MX records. | +| `ptr` | _[PTR](PTR/readme.md)_ array | `[]` | | Array of PTR records. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `soa` | _[SOA](SOA/readme.md)_ array | `[]` | | Array of SOA records. | +| `srv` | _[SRV](SRV/readme.md)_ array | `[]` | | Array of SRV records. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `txt` | _[TXT](TXT/readme.md)_ array | `[]` | | Array of TXT records. | +| `virtualNetworkLinks` | _[virtualNetworkLinks](virtualNetworkLinks/readme.md)_ array | `[]` | | Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'vnetResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private DNS zone. | +| `resourceGroupName` | string | The resource group the private DNS zone was deployed into. | +| `resourceId` | string | The resource ID of the private DNS zone. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module privateDnsZones './Microsoft.Network/privateDnsZones/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npdzcom' + params: { + // Required parameters + name: '<>npdzcom001.com' + // Non-required parameters + a: [ + { + aRecords: [ + { + ipv4Address: '10.240.4.4' + } + ] + name: 'A_10.240.4.4' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + aaaa: [ + { + aaaaRecords: [ + { + ipv6Address: '2001:0db8:85a3:0000:0000:8a2e:0370:7334' + } + ] + name: 'AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334' + ttl: 3600 + } + ] + cname: [ + { + cnameRecord: { + cname: 'test' + } + name: 'CNAME_test' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + mx: [ + { + mxRecords: [ + { + exchange: 'contoso.com' + preference: 100 + } + ] + name: 'MX_contoso' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + ptr: [ + { + name: 'PTR_contoso' + ptrRecords: [ + { + ptrdname: 'contoso.com' + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + } + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + soa: [ + { + name: '@' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + soaRecord: { + email: 'azureprivatedns-host.microsoft.com' + expireTime: 2419200 + host: 'azureprivatedns.net' + minimumTtl: 10 + refreshTime: 3600 + retryTime: 300 + serialNumber: '1' + } + ttl: 3600 + } + ] + srv: [ + { + name: 'SRV_contoso' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + srvRecords: [ + { + port: 9332 + priority: 0 + target: 'test.contoso.com' + weight: 0 + } + ] + ttl: 3600 + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + txt: [ + { + name: 'TXT_test' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + ttl: 3600 + txtRecords: [ + { + value: [ + 'test' + ] + } + ] + } + ] + virtualNetworkLinks: [ + { + registrationEnabled: true + virtualNetworkResourceId: '' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>npdzcom001.com" + }, + // Non-required parameters + "a": { + "value": [ + { + "aRecords": [ + { + "ipv4Address": "10.240.4.4" + } + ], + "name": "A_10.240.4.4", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "aaaa": { + "value": [ + { + "aaaaRecords": [ + { + "ipv6Address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + } + ], + "name": "AAAA_2001_0db8_85a3_0000_0000_8a2e_0370_7334", + "ttl": 3600 + } + ] + }, + "cname": { + "value": [ + { + "cnameRecord": { + "cname": "test" + }, + "name": "CNAME_test", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "mx": { + "value": [ + { + "mxRecords": [ + { + "exchange": "contoso.com", + "preference": 100 + } + ], + "name": "MX_contoso", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "ptr": { + "value": [ + { + "name": "PTR_contoso", + "ptrRecords": [ + { + "ptrdname": "contoso.com" + } + ], + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600 + } + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "soa": { + "value": [ + { + "name": "@", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "soaRecord": { + "email": "azureprivatedns-host.microsoft.com", + "expireTime": 2419200, + "host": "azureprivatedns.net", + "minimumTtl": 10, + "refreshTime": 3600, + "retryTime": 300, + "serialNumber": "1" + }, + "ttl": 3600 + } + ] + }, + "srv": { + "value": [ + { + "name": "SRV_contoso", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "srvRecords": [ + { + "port": 9332, + "priority": 0, + "target": "test.contoso.com", + "weight": 0 + } + ], + "ttl": 3600 + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "txt": { + "value": [ + { + "name": "TXT_test", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "ttl": 3600, + "txtRecords": [ + { + "value": [ + "test" + ] + } + ] + } + ] + }, + "virtualNetworkLinks": { + "value": [ + { + "registrationEnabled": true, + "virtualNetworkResourceId": "" + } + ] + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module privateDnsZones './Microsoft.Network/privateDnsZones/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npdzmin' + params: { + // Required parameters + name: '<>npdzmin001.com' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>npdzmin001.com" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep new file mode 100644 index 000000000..9bcfab827 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/deploy.bicep @@ -0,0 +1,61 @@ +@description('Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment.') +param privateDnsZoneName string + +@description('Optional. The name of the virtual network link.') +param name string = '${last(split(virtualNetworkResourceId, '/'))}-vnetlink' + +@description('Optional. The location of the PrivateDNSZone. Should be global.') +param location string = 'global' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?.') +param registrationEnabled bool = false + +@description('Required. Link to another virtual network resource ID.') +param virtualNetworkResourceId string + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = { + name: privateDnsZoneName +} + +resource virtualNetworkLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: name + parent: privateDnsZone + location: location + tags: tags + properties: { + registrationEnabled: registrationEnabled + virtualNetwork: { + id: virtualNetworkResourceId + } + } +} + +@description('The name of the deployed virtual network link.') +output name string = virtualNetworkLink.name + +@description('The resource ID of the deployed virtual network link.') +output resourceId string = virtualNetworkLink.id + +@description('The resource group of the deployed virtual network link.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = virtualNetworkLink.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md new file mode 100644 index 000000000..6f73f934b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/readme.md @@ -0,0 +1,95 @@ +# Private DNS Zone Virtual Network Link `[Microsoft.Network/privateDnsZones/virtualNetworkLinks]` + +This module deploys private dns zone virtual network links. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualNetworkResourceId` | string | Link to another virtual network resource ID. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDnsZoneName` | string | The name of the parent Private DNS zone. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `'global'` | The location of the PrivateDNSZone. Should be global. | +| `name` | string | `[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]` | The name of the virtual network link. | +| `registrationEnabled` | bool | `False` | Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?. | +| `tags` | object | `{object}` | Tags of the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed virtual network link. | +| `resourceGroupName` | string | The resource group of the deployed virtual network link. | +| `resourceId` | string | The resource ID of the deployed virtual network link. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateDnsZones/virtualNetworkLinks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..f2e141419 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateEndpoint.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateEndpoint +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/dependencies.bicep new file mode 100644 index 000000000..39fcecbe5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/dependencies.bicep @@ -0,0 +1,95 @@ +@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. The name of the Application Security Group to create.') +param applicationSecurityGroupName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +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 applicationSecurityGroup 'Microsoft.Network/applicationSecurityGroups@2022-05-01' = { + name: applicationSecurityGroupName + location: location +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.vaultcore.azure.net' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +@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 Key Vault.') +output keyVaultResourceId string = keyVault.id + +@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 + +@description('The resource ID of the created Application Security Group.') +output applicationSecurityGroupResourceId string = applicationSecurityGroup.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/deploy.test.bicep new file mode 100644 index 000000000..4851b0fcb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/common/deploy.test.bicep @@ -0,0 +1,93 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privateendpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npecom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + keyVaultName: 'dep-<>-kv-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + applicationSecurityGroupName: 'dep-<>-asg-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + groupIds: [ + 'vault' + ] + serviceResourceId: nestedDependencies.outputs.keyVaultResourceId + subnetResourceId: nestedDependencies.outputs.subnetResourceId + lock: 'CanNotDelete' + privateDnsZoneGroup: { + privateDNSResourceIds: [ + nestedDependencies.outputs.privateDNSZoneResourceId + ] + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + ipConfigurations: [ + { + name: 'myIPconfig' + properties: { + groupId: 'vault' + memberName: 'default' + privateIPAddress: '10.0.0.10' + } + } + ] + customNetworkInterfaceName: '<>${serviceShort}001nic' + applicationSecurityGroups: [ + { + id: nestedDependencies.outputs.applicationSecurityGroupResourceId + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/dependencies.bicep new file mode 100644 index 000000000..6237b3271 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/dependencies.bicep @@ -0,0 +1,54 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +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: [] + } +} + +@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 Key Vault.') +output keyVaultResourceId string = keyVault.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/deploy.test.bicep new file mode 100644 index 000000000..5316fd2c4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/.test/min/deploy.test.bicep @@ -0,0 +1,56 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privateendpoints-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npemin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + keyVaultName: 'dep-<>-kv-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + groupIds: [ + 'vault' + ] + serviceResourceId: nestedDependencies.outputs.keyVaultResourceId + subnetResourceId: nestedDependencies.outputs.subnetResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/deploy.bicep new file mode 100644 index 000000000..d8cab08d2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/deploy.bicep @@ -0,0 +1,132 @@ +@description('Required. Name of the private endpoint resource to create.') +param name string + +@description('Required. Resource ID of the subnet where the endpoint needs to be created.') +param subnetResourceId string + +@description('Required. Resource ID of the resource that needs to be connected to the network.') +param serviceResourceId string + +@description('Optional. Application security groups in which the private endpoint IP configuration is included.') +param applicationSecurityGroups array = [] + +@description('Optional. The custom name of the network interface attached to the private endpoint.') +param customNetworkInterfaceName string = '' + +@description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') +param ipConfigurations array = [] + +@description('Required. Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to.') +param groupIds array + +@description('Optional. The private DNS zone group configuration used to associate the private endpoint with one or multiple private DNS zones. A DNS zone group can support up to 5 DNS zones.') +param privateDnsZoneGroup object = {} + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags to be applied on all resources/resource groups in this deployment.') +param tags object = {} + +@description('Optional. Custom DNS configurations.') +param customDnsConfigs array = [] + +@description('Optional. Manual PrivateLink Service Connections.') +param manualPrivateLinkServiceConnections array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + applicationSecurityGroups: applicationSecurityGroups + customDnsConfigs: customDnsConfigs + customNetworkInterfaceName: customNetworkInterfaceName + ipConfigurations: ipConfigurations + manualPrivateLinkServiceConnections: manualPrivateLinkServiceConnections + privateLinkServiceConnections: [ + { + name: name + properties: { + privateLinkServiceId: serviceResourceId + groupIds: groupIds + } + } + ] + subnet: { + id: subnetResourceId + } + + } +} + +module privateEndpoint_privateDnsZoneGroup 'privateDnsZoneGroups/deploy.bicep' = if (!empty(privateDnsZoneGroup)) { + name: '${uniqueString(deployment().name, location)}-PrivateEndpoint-PrivateDnsZoneGroup' + params: { + privateDNSResourceIds: privateDnsZoneGroup.privateDNSResourceIds + privateEndpointName: privateEndpoint.name + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +resource privateEndpoint_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${privateEndpoint.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateEndpoint +} + +module privateEndpoint_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PrivateEndpoint-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateEndpoint.id + } +}] + +@description('The resource group the private endpoint was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the private endpoint.') +output resourceId string = privateEndpoint.id + +@description('The name of the private endpoint.') +output name string = privateEndpoint.name + +@description('The location the resource was deployed into.') +output location string = privateEndpoint.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep new file mode 100644 index 000000000..7f0064f68 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/deploy.bicep @@ -0,0 +1,53 @@ +@description('Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment.') +param privateEndpointName string + +@description('Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones.') +@minLength(1) +@maxLength(5) +param privateDNSResourceIds array + +@description('Optional. The name of the private DNS zone group.') +param name string = 'default' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +var privateDnsZoneConfigs = [for privateDNSResourceId in privateDNSResourceIds: { + name: last(split(privateDNSResourceId, '/'))! + properties: { + privateDnsZoneId: privateDNSResourceId + } +}] + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2022-07-01' existing = { + name: privateEndpointName +} + +resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = { + name: name + parent: privateEndpoint + properties: { + privateDnsZoneConfigs: privateDnsZoneConfigs + } +} + +@description('The name of the private endpoint DNS zone group.') +output name string = privateDnsZoneGroup.name + +@description('The resource ID of the private endpoint DNS zone group.') +output resourceId string = privateDnsZoneGroup.id + +@description('The resource group the private endpoint DNS zone group was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md new file mode 100644 index 000000000..ff2535e9c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/readme.md @@ -0,0 +1,50 @@ +# Network Private Endpoint Private DNS Zone Group `[Microsoft.Network/privateEndpoints/privateDnsZoneGroups]` + +This module deploys a private endpoint private DNS zone group + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints/privateDnsZoneGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateDNSResourceIds` | array | Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `privateEndpointName` | string | The name of the parent private endpoint. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `name` | string | `'default'` | The name of the private DNS zone group. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the private endpoint DNS zone group. | +| `resourceGroupName` | string | The resource group the private endpoint DNS zone group was deployed into. | +| `resourceId` | string | The resource ID of the private endpoint DNS zone group. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/privateDnsZoneGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/readme.md new file mode 100644 index 000000000..45d04b9a4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/readme.md @@ -0,0 +1,497 @@ +# Private Endpoints `[Microsoft.Network/privateEndpoints]` + +This template deploys a private endpoint for a generic service. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/privateEndpoints` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/privateEndpoints/privateDnsZoneGroups) | + +### Resource dependency + +The following resources are required to be able to deploy this resource: + +- `PrivateDNSZone` +- `VirtualNetwork/subnet` +- The service that needs to be connected through private endpoint + +**Important**: Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `groupIds` | array | Subtype(s) of the connection to be created. The allowed values depend on the type serviceResourceId refers to. | +| `name` | string | Name of the private endpoint resource to create. | +| `serviceResourceId` | string | Resource ID of the resource that needs to be connected to the network. | +| `subnetResourceId` | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `applicationSecurityGroups` | array | `[]` | | Application security groups in which the private endpoint IP configuration is included. | +| `customDnsConfigs` | array | `[]` | | Custom DNS configurations. | +| `customNetworkInterfaceName` | string | `''` | | The custom name of the network interface attached to the private endpoint. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ipConfigurations` | array | `[]` | | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `manualPrivateLinkServiceConnections` | array | `[]` | | Manual PrivateLink Service Connections. | +| `privateDnsZoneGroup` | object | `{object}` | | The private DNS zone group configuration used to associate the private endpoint with one or multiple private DNS zones. A DNS zone group can support up to 5 DNS zones. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags to be applied on all resources/resource groups in this deployment. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `applicationSecurityGroups` + +You can attach multiple Application Security Groups to a private endpoint resource. + +

+ +Parameter JSON format + +```json +"applicationSecurityGroups": { + "value": [ + { + "id": "" + }, + { + "id": "" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +applicationSecurityGroups: [ + { + id: '' + } + { + id: '' + } +] +``` + +
+

+ +### Parameter Usage: `customNetworkInterfaceName` + +You can customize the name of the private endpoint network interface instead of the default one that contains the string 'nic.GUID'. This helps with having consistent naming standards across all resources. Existing private endpoints cannot be renamed. See [documentation](https://learn.microsoft.com/en-us/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#network-interface-rename) for more details. + +

+ +Parameter JSON format + +```json +"customNetworkInterfaceName": { + "value": "myPrivateEndpointName-Nic" +} +``` + +
+ +
+ +Bicep format + +```bicep +customNetworkInterfaceName: 'myPrivateEndpointName-Nic' +``` + +
+

+ +### Parameter Usage: `ipConfigurations` + +You can use this property to define a static IP address for the private endpoint instead of the default dynamic one. To do that, first extract the `memberName` and `groupId` for the resource type you are creating the private endpoint for. See [documentation](https://learn.microsoft.com/en-us/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#determine-groupid-and-membername) for guidance on how to do that. Also provide the `privateIPAddress` for the private endpoint from the subnet range you are creating the private endpoint in. Note that static IP addresses [can be applied](https://learn.microsoft.com/en-us/azure/private-link/manage-private-endpoint?tabs=manage-private-link-powershell#custom-properties) when the private endpoint is created. + +

+ +Parameter JSON format + +```json +"customNetworkInterfaceName": { + "value": [ + { + "name": "myIPconfig", + "properties": { + "memberName": "", // e.g. default, sites, blob + "groupId": "", // e.g. vault, registry, blob + "privateIPAddress": "10.10.10.10" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +ipConfigurations: [ + { + name: 'myIPconfig' + properties: { + memberName: '' // e.g. default, sites, blob + groupId: '' // e.g. vault, registry, blob + privateIPAddress: '10.10.10.10' + } + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private endpoint. | +| `resourceGroupName` | string | The resource group the private endpoint was deployed into. | +| `resourceId` | string | The resource ID of the private endpoint. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module privateEndpoints './Microsoft.Network/privateEndpoints/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npecom' + params: { + // Required parameters + groupIds: [ + 'vault' + ] + name: '<>npecom001' + serviceResourceId: '' + subnetResourceId: '' + // Non-required parameters + applicationSecurityGroups: [ + { + id: '' + } + ] + customNetworkInterfaceName: '<>npecom001nic' + enableDefaultTelemetry: '' + ipConfigurations: [ + { + name: 'myIPconfig' + properties: { + groupId: 'vault' + memberName: 'default' + privateIPAddress: '10.0.0.10' + } + } + ] + lock: 'CanNotDelete' + privateDnsZoneGroup: { + privateDNSResourceIds: [ + '' + ] + } + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "groupIds": { + "value": [ + "vault" + ] + }, + "name": { + "value": "<>npecom001" + }, + "serviceResourceId": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "applicationSecurityGroups": { + "value": [ + { + "id": "" + } + ] + }, + "customNetworkInterfaceName": { + "value": "<>npecom001nic" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "ipConfigurations": { + "value": [ + { + "name": "myIPconfig", + "properties": { + "groupId": "vault", + "memberName": "default", + "privateIPAddress": "10.0.0.10" + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "privateDnsZoneGroup": { + "value": { + "privateDNSResourceIds": [ + "" + ] + } + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module privateEndpoints './Microsoft.Network/privateEndpoints/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npemin' + params: { + // Required parameters + groupIds: [ + 'vault' + ] + name: '<>npemin001' + serviceResourceId: '' + subnetResourceId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "groupIds": { + "value": [ + "vault" + ] + }, + "name": { + "value": "<>npemin001" + }, + "serviceResourceId": { + "value": "" + }, + "subnetResourceId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateEndpoints/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..3be88b5df --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource privateLinkService 'Microsoft.Network/privateLinkServices@2022-01-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(privateLinkService.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: privateLinkService +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/dependencies.bicep new file mode 100644 index 000000000..3bcae47f4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/dependencies.bicep @@ -0,0 +1,68 @@ +@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 Load Balancer to create.') +param loadBalancerName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2022-01-01' = { + name: loadBalancerName + location: location + sku: { + name: 'Standard' + } + properties: { + frontendIPConfigurations: [ + { + name: 'frontendIPConfiguration' + properties: { + subnet: { + id: virtualNetwork.properties.subnets[0].id + } + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@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 Load Balancer Frontend IP Configuration.') +output loadBalancerFrontendIpConfigurationResourceId string = loadBalancer.properties.frontendIPConfigurations[0].id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/deploy.test.bicep new file mode 100644 index 000000000..f303a62fa --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/common/deploy.test.bicep @@ -0,0 +1,97 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privatelinkservices-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nplscom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + loadBalancerName: 'dep-<>-lb-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + ipConfigurations: [ + { + name: '${serviceShort}01' + properties: { + primary: true + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: nestedDependencies.outputs.subnetResourceId + } + } + } + ] + loadBalancerFrontendIpConfigurations: [ + { + id: nestedDependencies.outputs.loadBalancerFrontendIpConfigurationResourceId + } + ] + autoApproval: { + subscriptions: [ + '*' + ] + } + visibility: { + subscriptions: [ + subscription().subscriptionId + ] + } + enableProxyProtocol: true + fqdns: [ + '${serviceShort}.plsfqdn01.azure.privatelinkservice' + '${serviceShort}.plsfqdn02.azure.privatelinkservice' + ] + roleAssignments: [ + { + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/dependencies.bicep new file mode 100644 index 000000000..7eb2bf4a4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/dependencies.bicep @@ -0,0 +1,57 @@ +@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 Load Balancer to create.') +param loadBalancerName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } +} + +resource loadBalancer 'Microsoft.Network/loadBalancers@2022-01-01' = { + name: loadBalancerName + location: location + sku: { + name: 'Standard' + } + properties: { + frontendIPConfigurations: [ + { + name: 'frontendIPConfiguration' + properties: { + subnet: { + id: virtualNetwork.properties.subnets[0].id + } + } + } + ] + } +} + +@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 Load Balancer Frontend IP Configuration.') +output loadBalancerFrontendIpConfigurationResourceId string = loadBalancer.properties.frontendIPConfigurations[0].id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/deploy.test.bicep new file mode 100644 index 000000000..c1766643a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/.test/min/deploy.test.bicep @@ -0,0 +1,66 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.privatelinkservices-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nplsmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + loadBalancerName: 'dep-<>-lb-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + ipConfigurations: [ + { + name: '${serviceShort}01' + properties: { + subnet: { + id: nestedDependencies.outputs.subnetResourceId + } + } + } + ] + loadBalancerFrontendIpConfigurations: [ + { + id: nestedDependencies.outputs.loadBalancerFrontendIpConfigurationResourceId + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/deploy.bicep new file mode 100644 index 000000000..3eeb50e23 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/deploy.bicep @@ -0,0 +1,104 @@ +@description('Required. Name of the private link service to create.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags to be applied on all resources/resource groups in this deployment.') +param tags object = {} + +@description('Optional. The extended location of the load balancer.') +param extendedLocation object = {} + +@description('Optional. The auto-approval list of the private link service.') +param autoApproval object = {} + +@description('Optional. Whether the private link service is enabled for proxy protocol or not.') +param enableProxyProtocol bool = false + +@description('Optional. The list of Fqdn.') +param fqdns array = [] + +@description('Optional. An array of private link service IP configurations.') +param ipConfigurations array = [] + +@description('Optional. An array of references to the load balancer IP configurations.') +param loadBalancerFrontendIpConfigurations array = [] + +@description('Optional. The visibility list of the private link service.') +param visibility object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource privateLinkService 'Microsoft.Network/privateLinkServices@2022-01-01' = { + name: name + location: location + tags: tags + extendedLocation: !empty(extendedLocation) ? extendedLocation : null + properties: { + autoApproval: autoApproval + enableProxyProtocol: enableProxyProtocol + fqdns: fqdns + ipConfigurations: ipConfigurations + loadBalancerFrontendIpConfigurations: loadBalancerFrontendIpConfigurations + visibility: visibility + } +} + +resource privateLinkService_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${privateLinkService.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: privateLinkService +} + +module privateLinkService_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PrivateLinkService-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: privateLinkService.id + } +}] + +@description('The resource group the private link service was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the private link service.') +output resourceId string = privateLinkService.id + +@description('The name of the private link service.') +output name string = privateLinkService.name + +@description('The location the resource was deployed into.') +output location string = privateLinkService.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/readme.md new file mode 100644 index 000000000..b1cb6ba50 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/readme.md @@ -0,0 +1,666 @@ +# Network PrivateLinkServices `[Microsoft.Network/privateLinkServices]` + +This module deploys Network Private Link Services. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/privateLinkServices` | [2022-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-01-01/privateLinkServices) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the private link service to create. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `autoApproval` | object | `{object}` | | The auto-approval list of the private link service. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableProxyProtocol` | bool | `False` | | Whether the private link service is enabled for proxy protocol or not. | +| `extendedLocation` | object | `{object}` | | The extended location of the load balancer. | +| `fqdns` | array | `[]` | | The list of Fqdn. | +| `ipConfigurations` | array | `[]` | | An array of private link service IP configurations. | +| `loadBalancerFrontendIpConfigurations` | array | `[]` | | An array of references to the load balancer IP configurations. | +| `location` | string | `[resourceGroup().location]` | | Location for all Resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags to be applied on all resources/resource groups in this deployment. | +| `visibility` | object | `{object}` | | The visibility list of the private link service. | + + +### Parameter Usage: `ipConfigurations` + +This property refers to the NAT (Network Address Translation) IP configuration for the Private Link service. The NAT IP can be chosen from any subnet in a service provider's virtual network. Private Link service performs destination side NAT-ing on the Private Link traffic. This ensures that there is no IP conflict between source (consumer side) and destination (service provider) address space. On the destination side (service provider side), the NAT IP address will show up as Source IP for all packets received by your service and destination IP for all packets sent by your service. + +

+ +Parameter JSON format + +```json +"ipConfigurations": { + "value": [ + // Example showing only mandatory fields + { + "name": "minpls01", // Name of the IP configuration + "properties": { + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + }, + // Example showing commonly used fields + { + "name": "pls01", // Name of the IP configuration + "properties": { + "primary": false, // Whether the ip configuration is primary or not + "privateIPAddressVersion": "IPv4", // Whether the specific IP configuration is IPv4 or IPv6. Default is IPv4 + "privateIPAllocationMethod": "Static", // The private IP address allocation method + "privateIPAddress": "10.0.1.10", // If "privateIPAllocationMethod" is set to "Static" then this needs to be supplied + "subnet": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001" // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +ipConfigurations: [ + // Example showing only mandatory fields + { + name: 'minpls01' // Name of the IP configuration + properties: { + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + } + // Example showing commonly used fields + { + name: 'pls01' // Name of the IP configuration + properties: { + primary: false // Whether the ip configuration is primary or not + privateIPAddressVersion: 'IPv4' // Whether the specific IP configuration is IPv4 or IPv6. Default is IPv4 + privateIPAllocationMethod: 'Static' // Whether the specific IP configuration is IPv4 or IPv6. Default is IPv4 + privateIPAddress: '10.0.1.10' // If "privateIPAllocationMethod" is set to "Static" then this needs to be supplied + subnet: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-001' // The subnet selected here will be used by the Private Link Service to pick up the NAT IP + } + } + } +] +``` + +
+

+ +### Parameter Usage: `loadBalancerFrontendIpConfigurations` + +Private Link service is tied to the frontend IP address of a Standard Load Balancer. All traffic destined for the service will reach the frontend of the SLB. You can configure SLB rules to direct this traffic to appropriate backend pools where your applications are running. Load balancer frontend IP configurations are different than NAT IP configurations. + +

+ +Parameter JSON format + +```json +"loadBalancerFrontendIpConfigurations": { + "value": [ + // Example showing reference to the font end IP configuration of the load balancer + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/frontendIPConfigurations/privateIPConfig1" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +loadBalancerFrontendIpConfigurations: [ + // Example showing reference to the font end IP configuration of the load balancer + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/loadBalancers/adp-<>-az-lb-internal-001/frontendIPConfigurations/privateIPConfig1' + } +] +``` + +
+

+ +### Parameter Usage: `extendedLocation` + +This is the Edge Zone ID of the Edge Zone corresponding to the region in which the resource is deployed. More information is available here: [Azure Edge Zone ID](https://learn.microsoft.com/en-us/azure/public-multi-access-edge-compute-mec/key-concepts#azure-edge-zone-id). + +

+ +Parameter JSON format + +```json +"extendedLocation": { + // Example showing usage of the extendedLocation param + "value": { + "name": "attatlanta1", // Edge Zone ID for the parent East US 2 region is "attatlanta1" + "type": "EdgeZone" // Fixed value + } +} +``` + +
+ +
+ +Bicep format + +```bicep +extendedLocation: { + // Example showing usage of the extendedLocation param + name: 'attdallas1' // Edge Zone ID for the parent South Central US region is "attdallas1". + type: 'EdgeZone' // Fixed value +} +``` + +
+

+ +### Parameter Usage: `autoApproval` + +Auto-approval controls the automated access to the Private Link service. The subscriptions specified in the auto-approval list are approved automatically when a connection is requested from private endpoints in those subscriptions. + +

+ +Parameter JSON format + +```json +// Example to auto-approve for all the subscriptions present under the "visibility" param +"autoApproval": { + "value": [ + "*" + ] +} + +// Example to auto-approve a specific set of subscriptions. This should always be a subset of the subscriptions provided under the "visibility" param +"autoApproval": { + "value": [ + "12345678-1234-1234-1234-123456781234", // Subscription 1 + "87654321-1234-1234-1234-123456781234" // Subscription 2 + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +// Example to auto-approve for all the subscriptions present under the "visibility" param +autoApproval: [ + "*" +] + +// Example to auto-approve a specific set of subscriptions. This should always be a subset of the subscriptions provided under "visibility" +autoApproval: [ + '12345678-1234-1234-1234-123456781234' // Subscription 1 + '87654321-1234-1234-1234-123456781234' // Subscription 2 +] +``` + +
+

+ +### Parameter Usage: `visibility` + +Visibility is the property that controls the exposure settings for your Private Link service. Service providers can choose to limit the exposure to their service to subscriptions with Azure role-based access control (Azure RBAC) permissions, a restricted set of subscriptions, or all Azure subscriptions. + +

+ +Parameter JSON format + +```json +"visibility": { + "value" + // Example showing usage of visibility param + "subscriptions": [ + "12345678-1234-1234-1234-123456781234", // Subscription 1 + "87654321-1234-1234-1234-123456781234", // Subscription 2 + "12341234-1234-1234-1234-123456781234" // Subscription 3 + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +visibility: { + subscriptions: [ + '12345678-1234-1234-1234-123456781234' // Subscription 1 + '87654321-1234-1234-1234-123456781234' // Subscription 2 + '12341234-1234-1234-1234-123456781234' // Subscription 3 + ] +} +``` + +
+

+ +### Parameter Usage: `enableProxyProtocol` + +This property lets the service provider use tcp proxy v2 to retrieve connection information about the service consumer. Service Provider is responsible for setting up receiver configs to be able to parse the proxy protocol v2 header. + +### Parameter Usage: `fqdns` + +This property lets you set the fqdn(s) to access the Private Link service. +

+ +Parameter JSON format + +```json +"fqdns": { + // Example to set FQDNs for the Private Link service + "value": [ + "pls01.azure.privatelinkservice", // FQDN 1 + "pls01-duplicate.azure.privatelinkserivce" // FQDN 2 + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +fqdns: [ + // Example to set FQDNs for the Private Link service + 'pls01.azure.privatelinkservice' + 'pls01-duplicate.azure.privatelinkservice' +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the private link service. | +| `resourceGroupName` | string | The resource group the private link service was deployed into. | +| `resourceId` | string | The resource ID of the private link service. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module privateLinkServices './Microsoft.Network/privateLinkServices/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nplscom' + params: { + // Required parameters + name: '<>nplscom001' + // Non-required parameters + autoApproval: { + subscriptions: [ + '*' + ] + } + enableDefaultTelemetry: '' + enableProxyProtocol: true + fqdns: [ + 'nplscom.plsfqdn01.azure.privatelinkservice' + 'nplscom.plsfqdn02.azure.privatelinkservice' + ] + ipConfigurations: [ + { + name: 'nplscom01' + properties: { + primary: true + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '' + } + } + } + ] + loadBalancerFrontendIpConfigurations: [ + { + id: '' + } + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + visibility: { + subscriptions: [ + '' + ] + } + } +} +``` + +
+

+ +

+ +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": "<>nplscom001" + }, + // Non-required parameters + "autoApproval": { + "value": { + "subscriptions": [ + "*" + ] + } + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enableProxyProtocol": { + "value": true + }, + "fqdns": { + "value": [ + "nplscom.plsfqdn01.azure.privatelinkservice", + "nplscom.plsfqdn02.azure.privatelinkservice" + ] + }, + "ipConfigurations": { + "value": [ + { + "name": "nplscom01", + "properties": { + "primary": true, + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "" + } + } + } + ] + }, + "loadBalancerFrontendIpConfigurations": { + "value": [ + { + "id": "" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "visibility": { + "value": { + "subscriptions": [ + "" + ] + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module privateLinkServices './Microsoft.Network/privateLinkServices/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nplsmin' + params: { + // Required parameters + name: '<>nplsmin001' + // Non-required parameters + enableDefaultTelemetry: '' + ipConfigurations: [ + { + name: 'nplsmin01' + properties: { + subnet: { + id: '' + } + } + } + ] + loadBalancerFrontendIpConfigurations: [ + { + id: '' + } + ] + } +} +``` + +
+

+ +

+ +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": "<>nplsmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "ipConfigurations": { + "value": [ + { + "name": "nplsmin01", + "properties": { + "subnet": { + "id": "" + } + } + } + ] + }, + "loadBalancerFrontendIpConfigurations": { + "value": [ + { + "id": "" + } + ] + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/privateLinkServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..a078fac39 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource publicIpAddress 'Microsoft.Network/publicIPAddresses@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(publicIpAddress.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: publicIpAddress +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/deploy.test.bicep new file mode 100644 index 000000000..57bea0295 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/common/deploy.test.bicep @@ -0,0 +1,90 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.publicipaddresses-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npiacom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + publicIPAllocationMethod: 'Static' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + skuName: 'Standard' + zones: [ + '1' + '2' + '3' + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/min/deploy.test.bicep new file mode 100644 index 000000000..c35a30ac4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.publicipaddresses-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npiamin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/deploy.bicep new file mode 100644 index 000000000..1d7e4057a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/deploy.bicep @@ -0,0 +1,221 @@ +@description('Required. The name of the Public IP Address.') +param name string + +@description('Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix.') +param publicIPPrefixResourceId string = '' + +@description('Optional. The public IP address allocation method.') +@allowed([ + 'Dynamic' + 'Static' +]) +param publicIPAllocationMethod string = 'Dynamic' + +@description('Optional. Name of a public IP address SKU.') +@allowed([ + 'Basic' + 'Standard' +]) +param skuName string = 'Basic' + +@description('Optional. Tier of a public IP address SKU.') +@allowed([ + 'Global' + 'Regional' +]) +param skuTier string = 'Regional' + +@description('Optional. A list of availability zones denoting the IP allocated for the resource needs to come from.') +param zones array = [] + +@description('Optional. IP address version.') +@allowed([ + 'IPv4' + 'IPv6' +]) +param publicIPAddressVersion string = 'IPv4' + +// @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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system.') +param domainNameLabel string = '' + +@description('Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone.') +param fqdn string = '' + +@description('Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN.') +param reverseFqdn string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource publicIpAddress 'Microsoft.Network/publicIPAddresses@2022-07-01' = { + name: name + location: location + tags: tags + sku: { + name: skuName + tier: skuTier + } + zones: zones + properties: { + dnsSettings: !empty(domainNameLabel) ? { + domainNameLabel: domainNameLabel + fqdn: fqdn + reverseFqdn: reverseFqdn + } : null + publicIPAddressVersion: publicIPAddressVersion + publicIPAllocationMethod: publicIPAllocationMethod + publicIPPrefix: !empty(publicIPPrefixResourceId) ? { + id: publicIPPrefixResourceId + } : null + idleTimeoutInMinutes: 4 + ipTags: [] + } +} + +resource publicIpAddress_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${publicIpAddress.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: publicIpAddress +} + +resource publicIpAddress_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: publicIpAddress +} + +module publicIpAddress_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PIPAddress-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: publicIpAddress.id + } +}] + +@description('The resource group the public IP address was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the public IP address.') +output name string = publicIpAddress.name + +@description('The resource ID of the public IP address.') +output resourceId string = publicIpAddress.id + +@description('The public IP address of the public IP address resource.') +output ipAddress string = contains(publicIpAddress.properties, 'ipAddress') ? publicIpAddress.properties.ipAddress : '' + +@description('The location the resource was deployed into.') +output location string = publicIpAddress.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/readme.md new file mode 100644 index 000000000..de3d7758c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/readme.md @@ -0,0 +1,339 @@ +# Public IP Addresses `[Microsoft.Network/publicIPAddresses]` + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/publicIPAddresses` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/publicIPAddresses) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Public IP Address. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `domainNameLabel` | string | `''` | | The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `fqdn` | string | `''` | | The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicIPAddressVersion` | string | `'IPv4'` | `[IPv4, IPv6]` | IP address version. | +| `publicIPAllocationMethod` | string | `'Dynamic'` | `[Dynamic, Static]` | The public IP address allocation method. | +| `publicIPPrefixResourceId` | string | `''` | | Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix. | +| `reverseFqdn` | string | `''` | | The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `skuName` | string | `'Basic'` | `[Basic, Standard]` | Name of a public IP address SKU. | +| `skuTier` | string | `'Regional'` | `[Global, Regional]` | Tier of a public IP address SKU. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `zones` | array | `[]` | | A list of availability zones denoting the IP allocated for the resource needs to come from. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `ipAddress` | string | The public IP address of the public IP address resource. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the public IP address. | +| `resourceGroupName` | string | The resource group the public IP address was deployed into. | +| `resourceId` | string | The resource ID of the public IP address. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module publicIPAddresses './Microsoft.Network/publicIPAddresses/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npiacom' + params: { + // Required parameters + name: '<>npiacom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + publicIPAllocationMethod: 'Static' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + skuName: 'Standard' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + 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": "<>npiacom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "publicIPAllocationMethod": { + "value": "Static" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "skuName": { + "value": "Standard" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "zones": { + "value": [ + "1", + "2", + "3" + ] + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module publicIPAddresses './Microsoft.Network/publicIPAddresses/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npiamin' + params: { + // Required parameters + name: '<>npiamin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>npiamin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPAddresses/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..25739f5cb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource publicIpPrefix 'Microsoft.Network/publicIPPrefixes@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(publicIpPrefix.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: publicIpPrefix +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/deploy.test.bicep new file mode 100644 index 000000000..901f97600 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/common/deploy.test.bicep @@ -0,0 +1,65 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.publicipprefixes-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npipcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + prefixLength: 28 + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/min/deploy.test.bicep new file mode 100644 index 000000000..4a68c2e33 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.publicipprefixes-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'npipmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + prefixLength: 28 + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/deploy.bicep new file mode 100644 index 000000000..14a03bfa6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/deploy.bicep @@ -0,0 +1,91 @@ +@description('Required. Name of the Public IP Prefix.') +@minLength(1) +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Length of the Public IP Prefix.') +@minValue(28) +@maxValue(31) +param prefixLength int + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The customIpPrefix that this prefix is associated with. A custom IP address prefix is a contiguous range of IP addresses owned by an external customer and provisioned into a subscription. When a custom IP prefix is in Provisioned, Commissioning, or Commissioned state, a linked public IP prefix can be created. Either as a subset of the custom IP prefix range or the entire range.') +param customIPPrefix object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource publicIpPrefix 'Microsoft.Network/publicIPPrefixes@2022-07-01' = { + name: name + location: location + tags: tags + sku: { + name: 'Standard' + } + properties: { + customIPPrefix: !empty(customIPPrefix) ? customIPPrefix : null + publicIPAddressVersion: 'IPv4' + prefixLength: prefixLength + } +} + +resource publicIpPrefix_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${publicIpPrefix.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: publicIpPrefix +} + +module publicIpPrefix_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-PIPPrefix-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: publicIpPrefix.id + } +}] + +@description('The resource ID of the public IP prefix.') +output resourceId string = publicIpPrefix.id + +@description('The resource group the public IP prefix was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the public IP prefix.') +output name string = publicIpPrefix.name + +@description('The location the resource was deployed into.') +output location string = publicIpPrefix.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/readme.md new file mode 100644 index 000000000..e6a1610be --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/readme.md @@ -0,0 +1,292 @@ +# Public IP Prefixes `[Microsoft.Network/publicIPPrefixes]` + +This template deploys a public IP prefix. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/publicIPPrefixes` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/publicIPPrefixes) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Public IP Prefix. | +| `prefixLength` | int | Length of the Public IP Prefix. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `customIPPrefix` | object | `{object}` | | The customIpPrefix that this prefix is associated with. A custom IP address prefix is a contiguous range of IP addresses owned by an external customer and provisioned into a subscription. When a custom IP prefix is in Provisioned, Commissioning, or Commissioned state, a linked public IP prefix can be created. Either as a subset of the custom IP prefix range or the entire range. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the public IP prefix. | +| `resourceGroupName` | string | The resource group the public IP prefix was deployed into. | +| `resourceId` | string | The resource ID of the public IP prefix. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module publicIPPrefixes './Microsoft.Network/publicIPPrefixes/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npipcom' + params: { + // Required parameters + name: '<>npipcom001' + prefixLength: 28 + // Non-required parameters + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>npipcom001" + }, + "prefixLength": { + "value": 28 + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module publicIPPrefixes './Microsoft.Network/publicIPPrefixes/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-npipmin' + params: { + // Required parameters + name: '<>npipmin001' + prefixLength: 28 + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>npipmin001" + }, + "prefixLength": { + "value": 28 + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/publicIPPrefixes/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..98437ecf0 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource routeTable 'Microsoft.Network/routeTables@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(routeTable.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: routeTable +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/deploy.test.bicep new file mode 100644 index 000000000..a6c2288d6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/common/deploy.test.bicep @@ -0,0 +1,74 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.routetables-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nrtcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + routes: [ + { + name: 'default' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopIpAddress: '172.16.0.20' + nextHopType: 'VirtualAppliance' + } + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/min/deploy.test.bicep new file mode 100644 index 000000000..ac3b1b3c7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.routetables-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nrtmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/deploy.bicep new file mode 100644 index 000000000..26b97d1a7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/deploy.bicep @@ -0,0 +1,84 @@ +@description('Required. Name given for the hub route table.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. An Array of Routes to be established within the hub route table.') +param routes array = [] + +@description('Optional. Switch to disable BGP route propagation.') +param disableBgpRoutePropagation bool = false + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource routeTable 'Microsoft.Network/routeTables@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + routes: routes + disableBgpRoutePropagation: disableBgpRoutePropagation + } +} + +resource routeTable_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${routeTable.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: routeTable +} + +module routeTable_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-RouteTable-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: routeTable.id + } +}] + +@description('The resource group the route table was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the route table.') +output name string = routeTable.name + +@description('The resource ID of the route table.') +output resourceId string = routeTable.id + +@description('The location the resource was deployed into.') +output location string = routeTable.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/readme.md new file mode 100644 index 000000000..8cbc59b5c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/readme.md @@ -0,0 +1,395 @@ +# Route Tables `[Microsoft.Network/routeTables]` + +This module deploys a user defined route table. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/routeTables` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/routeTables) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name given for the hub route table. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `disableBgpRoutePropagation` | bool | `False` | | Switch to disable BGP route propagation. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `routes` | array | `[]` | | An Array of Routes to be established within the hub route table. | +| `tags` | object | `{object}` | | Tags of the resource. | + + +### Parameter Usage: `routes` + +The `routes` parameter accepts a JSON Array of Route objects to deploy to the Route Table. + +Here's an example of specifying a few routes: + +

+ +Parameter JSON format + +```json +"routes": { + "value": [ + { + "name": "tojumpboxes", + "properties": { + "addressPrefix": "172.16.0.48/28", + "nextHopType": "VnetLocal" + } + }, + { + "name": "tosharedservices", + "properties": { + "addressPrefix": "172.16.0.64/27", + "nextHopType": "VnetLocal" + } + }, + { + "name": "toonprem", + "properties": { + "addressPrefix": "10.0.0.0/8", + "nextHopType": "VirtualNetworkGateway" + } + }, + { + "name": "tonva", + "properties": { + "addressPrefix": "172.16.0.0/18", + "nextHopType": "VirtualAppliance", + "nextHopIpAddress": "172.16.0.20" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +routes: [ + { + name: 'tojumpboxes' + properties: { + addressPrefix: '172.16.0.48/28' + nextHopType: 'VnetLocal' + } + } + { + name: 'tosharedservices' + properties: { + addressPrefix: '172.16.0.64/27' + nextHopType: 'VnetLocal' + } + } + { + name: 'toonprem' + properties: { + addressPrefix: '10.0.0.0/8' + nextHopType: 'VirtualNetworkGateway' + } + } + { + name: 'tonva' + properties: { + addressPrefix: '172.16.0.0/18' + nextHopType: 'VirtualAppliance' + nextHopIpAddress: '172.16.0.20' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the route table. | +| `resourceGroupName` | string | The resource group the route table was deployed into. | +| `resourceId` | string | The resource ID of the route table. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module routeTables './Microsoft.Network/routeTables/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nrtcom' + params: { + // Required parameters + name: '<>nrtcom001' + // Non-required parameters + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + routes: [ + { + name: 'default' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopIpAddress: '172.16.0.20' + nextHopType: 'VirtualAppliance' + } + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nrtcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "routes": { + "value": [ + { + "name": "default", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopIpAddress": "172.16.0.20", + "nextHopType": "VirtualAppliance" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module routeTables './Microsoft.Network/routeTables/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nrtmin' + params: { + // Required parameters + name: '<>nrtmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nrtmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/routeTables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..4ef17ff89 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource trafficmanagerprofile 'Microsoft.Network/trafficmanagerprofiles@2018-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(trafficmanagerprofile.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: trafficmanagerprofile +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/deploy.test.bicep new file mode 100644 index 000000000..eacedcee5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/common/deploy.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.trafficmanagerprofiles-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ntmpcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // +var resourceName = '<>${serviceShort}001' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: resourceName + relativeName: resourceName + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/min/deploy.test.bicep new file mode 100644 index 000000000..00f47b45a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/.test/min/deploy.test.bicep @@ -0,0 +1,43 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.trafficmanagerprofiles-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'ntmpmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // +var resourceName = '<>${serviceShort}001' +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: resourceName + relativeName: resourceName + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/deploy.bicep new file mode 100644 index 000000000..7a861ae47 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/deploy.bicep @@ -0,0 +1,205 @@ +@description('Required. Name of the Traffic Manager.') +@minLength(1) +param name string + +@description('Optional. The status of the Traffic Manager profile.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param profileStatus string = 'Enabled' + +@description('Optional. The traffic routing method of the Traffic Manager profile.') +@allowed([ + 'Performance' + 'Priority' + 'Weighted' + 'Geographic' + 'MultiValue' + 'Subnet' +]) +param trafficRoutingMethod string = 'Performance' + +@description('Required. The relative DNS name provided by this Traffic Manager profile. This value is combined with the DNS domain name used by Azure Traffic Manager to form the fully-qualified domain name (FQDN) of the profile.') +param relativeName string + +@description('Optional. The DNS Time-To-Live (TTL), in seconds. This informs the local DNS resolvers and DNS clients how long to cache DNS responses provided by this Traffic Manager profile.') +param ttl int = 60 + +@description('Optional. The endpoint monitoring settings of the Traffic Manager profile.') +param monitorConfig object = { + protocol: 'http' + port: '80' + path: '/' +} + +@description('Optional. The list of endpoints in the Traffic Manager profile.') +param endpoints array = [] + +@description('Optional. Indicates whether Traffic View is \'Enabled\' or \'Disabled\' for the Traffic Manager profile. Null, indicates \'Disabled\'. Enabling this feature will increase the cost of the Traffic Manage profile.') +@allowed([ + 'Disabled' + 'Enabled' +]) +param trafficViewEnrollmentStatus string = 'Disabled' + +@description('Optional. Maximum number of endpoints to be returned for MultiValue routing type.') +param maxReturn int = 1 + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Resource tags.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'ProbeHealthStatusEvents' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource trafficManagerProfile 'Microsoft.Network/trafficmanagerprofiles@2018-08-01' = { + name: name + tags: tags + location: 'global' + properties: { + profileStatus: profileStatus + trafficRoutingMethod: trafficRoutingMethod + dnsConfig: { + relativeName: relativeName + ttl: ttl + } + monitorConfig: monitorConfig + endpoints: endpoints + trafficViewEnrollmentStatus: trafficViewEnrollmentStatus + maxReturn: maxReturn + } +} + +resource trafficManagerProfile_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${trafficManagerProfile.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: trafficManagerProfile +} + +resource trafficManagerProfile_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: trafficManagerProfile +} + +module trafficManagerProfile_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name)}-TrafficManagerProfile-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: trafficManagerProfile.id + } +}] + +@description('The resource ID of the traffic manager.') +output resourceId string = trafficManagerProfile.id + +@description('The resource group the traffic manager was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the traffic manager was deployed into.') +output name string = trafficManagerProfile.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/readme.md new file mode 100644 index 000000000..591f316c1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/readme.md @@ -0,0 +1,415 @@ +# Traffic Manager Profiles `[Microsoft.Network/trafficmanagerprofiles]` + +This module deploys a traffic manager profile. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/trafficmanagerprofiles` | [2018-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2018-08-01/trafficmanagerprofiles) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Traffic Manager. | +| `relativeName` | string | The relative DNS name provided by this Traffic Manager profile. This value is combined with the DNS domain name used by Azure Traffic Manager to form the fully-qualified domain name (FQDN) of the profile. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, ProbeHealthStatusEvents]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `endpoints` | array | `[]` | | The list of endpoints in the Traffic Manager profile. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `maxReturn` | int | `1` | | Maximum number of endpoints to be returned for MultiValue routing type. | +| `monitorConfig` | object | `{object}` | | The endpoint monitoring settings of the Traffic Manager profile. | +| `profileStatus` | string | `'Enabled'` | `[Disabled, Enabled]` | The status of the Traffic Manager profile. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Resource tags. | +| `trafficRoutingMethod` | string | `'Performance'` | `[Geographic, MultiValue, Performance, Priority, Subnet, Weighted]` | The traffic routing method of the Traffic Manager profile. | +| `trafficViewEnrollmentStatus` | string | `'Disabled'` | `[Disabled, Enabled]` | Indicates whether Traffic View is 'Enabled' or 'Disabled' for the Traffic Manager profile. Null, indicates 'Disabled'. Enabling this feature will increase the cost of the Traffic Manage profile. | +| `ttl` | int | `60` | | The DNS Time-To-Live (TTL), in seconds. This informs the local DNS resolvers and DNS clients how long to cache DNS responses provided by this Traffic Manager profile. | + + +### Parameter Usage: `monitorConfig` + +

+ +Parameter JSON format + +```json +"monitorConfig": { + "value": { + "protocol": "http", + "port": "80", + "path": "/" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +monitorConfig: { + protocol: 'http' + port: '80' + path: '/' +} +``` + +
+

+ +### Parameter Usage: `endpoints` + +

+ +Parameter JSON format + +```json +"endpoints": { + "value": [ + { + "id": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/trafficManagerProfiles//azureEndpoints/", + "name": "MyEndpoint001", + "type": "Microsoft.Network/trafficManagerProfiles/azureEndpoints", + "properties": + { + "endpointStatus": "Enabled", + "endpointMonitorStatus": "CheckingEndpoint", + "targetResourceId": "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/publicIPAddresses/", + "target": "my-pip-001.eastus.cloudapp.azure.com", + "weight": 1, + "priority": 1, + "endpointLocation": "East US" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +endpoints: [ + { + id: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/trafficManagerProfiles//azureEndpoints/' + name: 'MyEndpoint001' + type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints' + properties: + { + endpointStatus: 'Enabled' + endpointMonitorStatus: 'CheckingEndpoint' + targetResourceId: '/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups//providers/Microsoft.Network/publicIPAddresses/' + target: 'my-pip-001.eastus.cloudapp.azure.com' + weight: 1 + priority: 1 + endpointLocation: 'East US' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the traffic manager was deployed into. | +| `resourceGroupName` | string | The resource group the traffic manager was deployed into. | +| `resourceId` | string | The resource ID of the traffic manager. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module trafficmanagerprofiles './Microsoft.Network/trafficmanagerprofiles/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ntmpcom' + params: { + // Required parameters + name: '' + relativeName: '' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "" + }, + "relativeName": { + "value": "" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module trafficmanagerprofiles './Microsoft.Network/trafficmanagerprofiles/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-ntmpmin' + params: { + // Required parameters + name: '' + relativeName: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "" + }, + "relativeName": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/trafficmanagerprofiles/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/dependencies.bicep new file mode 100644 index 000000000..1c86b0f68 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/dependencies.bicep @@ -0,0 +1,42 @@ +@description('Required. The name of the Virtual WAN to create.') +param virtualWANName string + +@description('Required. The name of the Virtual Network to create.') +param virtualNetworkName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '10.0.0.0/16' + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + + addressPrefix: addressPrefix + } + } + ] + } +} + +@description('The resource ID of the created Virtual WAN.') +output virtualWWANResourceId string = virtualWan.id + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/deploy.test.bicep new file mode 100644 index 000000000..7cc742fdd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/common/deploy.test.bicep @@ -0,0 +1,84 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualHub-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvhcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualWANName: 'dep-<>-vw-${serviceShort}' + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>-${serviceShort}' + lock: 'CanNotDelete' + addressPrefix: '10.1.0.0/16' + virtualWanId: nestedDependencies.outputs.virtualWWANResourceId + hubRouteTables: [ + { + name: 'routeTable1' + } + ] + hubVirtualNetworkConnections: [ + { + name: 'connection1' + remoteVirtualNetworkId: nestedDependencies.outputs.virtualNetworkResourceId + routingConfiguration: { + associatedRouteTable: { + id: '${resourceGroup.id}/providers/Microsoft.Network/virtualHubs/<>-${serviceShort}/hubRouteTables/routeTable1' + } + propagatedRouteTables: { + ids: [ + { + id: '${resourceGroup.id}/providers/Microsoft.Network/virtualHubs/<>-${serviceShort}/hubRouteTables/routeTable1' + } + ] + labels: [ + 'none' + ] + } + } + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/dependencies.bicep new file mode 100644 index 000000000..6b1819ebe --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the virtual WAN to create.') +param virtualWANName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +@description('The resource ID of the created Virtual WAN.') +output virtualWWANResourceId string = virtualWan.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/deploy.test.bicep new file mode 100644 index 000000000..8986cf99f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/.test/min/deploy.test.bicep @@ -0,0 +1,52 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualHub-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvhmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualWANName: 'dep-<>-vw-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>-${serviceShort}' + addressPrefix: '10.0.0.0/16' + virtualWanId: nestedDependencies.outputs.virtualWWANResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/deploy.bicep new file mode 100644 index 000000000..e679bfb37 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/deploy.bicep @@ -0,0 +1,172 @@ +@description('Required. The virtual hub name.') +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. Address-prefix for this VirtualHub.') +param addressPrefix string + +@description('Optional. Flag to control transit for VirtualRouter hub.') +param allowBranchToBranchTraffic bool = true + +@description('Optional. Resource ID of the Express Route Gateway to link to.') +param expressRouteGatewayId string = '' + +@description('Optional. Resource ID of the Point-to-Site VPN Gateway to link to.') +param p2SVpnGatewayId string = '' + +@description('Optional. The preferred routing gateway types.') +@allowed([ + 'ExpressRoute' + 'None' + 'VpnGateway' + '' +]) +param preferredRoutingGateway string = '' + +@description('Optional. VirtualHub route tables.') +param routeTableRoutes array = [] + +@description('Optional. ID of the Security Partner Provider to link to.') +param securityPartnerProviderId string = '' + +@description('Optional. The Security Provider name.') +param securityProviderName string = '' + +@allowed([ + 'Basic' + 'Standard' +]) +@description('Optional. The sku of this VirtualHub.') +param sku string = 'Standard' + +@description('Optional. List of all virtual hub route table v2s associated with this VirtualHub.') +param virtualHubRouteTableV2s array = [] + +@description('Optional. VirtualRouter ASN.') +param virtualRouterAsn int = -1 + +@description('Optional. VirtualRouter IPs.') +param virtualRouterIps array = [] + +@description('Required. Resource ID of the virtual WAN to link to.') +param virtualWanId string + +@description('Optional. Resource ID of the VPN Gateway to link to.') +param vpnGatewayId string = '' + +@description('Optional. Route tables to create for the virtual hub.') +param hubRouteTables array = [] + +@description('Optional. Virtual network connections to create for the virtual hub.') +param hubVirtualNetworkConnections array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-05-01' = { + name: name + location: location + tags: tags + properties: { + addressPrefix: addressPrefix + allowBranchToBranchTraffic: allowBranchToBranchTraffic + expressRouteGateway: !empty(expressRouteGatewayId) ? { + id: expressRouteGatewayId + } : null + p2SVpnGateway: !empty(p2SVpnGatewayId) ? { + id: p2SVpnGatewayId + } : null + preferredRoutingGateway: !empty(preferredRoutingGateway) ? any(preferredRoutingGateway) : null + routeTable: !empty(routeTableRoutes) ? { + routes: routeTableRoutes + } : null + securityPartnerProvider: !empty(securityPartnerProviderId) ? { + id: securityPartnerProviderId + } : null + securityProviderName: securityProviderName + sku: sku + virtualHubRouteTableV2s: virtualHubRouteTableV2s + virtualRouterAsn: virtualRouterAsn != -1 ? virtualRouterAsn : null + virtualRouterIps: !empty(virtualRouterIps) ? virtualRouterIps : null + virtualWan: { + id: virtualWanId + } + vpnGateway: !empty(vpnGatewayId) ? { + id: vpnGatewayId + } : null + } +} + +resource virtualHub_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${virtualHub.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualHub +} + +module virtualHub_routeTables 'hubRouteTables/deploy.bicep' = [for (routeTable, index) in hubRouteTables: { + name: '${uniqueString(deployment().name, location)}-routeTable-${index}' + params: { + virtualHubName: virtualHub.name + name: routeTable.name + labels: contains(routeTable, 'labels') ? routeTable.labels : [] + routes: contains(routeTable, 'routes') ? routeTable.routes : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module virtualHub_hubVirtualNetworkConnections 'hubVirtualNetworkConnections/deploy.bicep' = [for (virtualNetworkConnection, index) in hubVirtualNetworkConnections: { + name: '${uniqueString(deployment().name, location)}-connection-${index}' + params: { + virtualHubName: virtualHub.name + name: virtualNetworkConnection.name + enableInternetSecurity: contains(virtualNetworkConnection, 'enableInternetSecurity') ? virtualNetworkConnection.enableInternetSecurity : true + remoteVirtualNetworkId: virtualNetworkConnection.remoteVirtualNetworkId + routingConfiguration: contains(virtualNetworkConnection, 'routingConfiguration') ? virtualNetworkConnection.routingConfiguration : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + virtualHub_routeTables + ] +}] + +@description('The resource group the virtual hub was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the virtual hub.') +output resourceId string = virtualHub.id + +@description('The name of the virtual hub.') +output name string = virtualHub.name + +@description('The location the resource was deployed into.') +output location string = virtualHub.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep new file mode 100644 index 000000000..11c4baa90 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/deploy.bicep @@ -0,0 +1,48 @@ +@description('Required. The route table name.') +param name string + +@description('Conditional. The name of the parent virtual hub. Required if the template is used in a standalone deployment.') +param virtualHubName string + +@description('Optional. List of labels associated with this route table.') +param labels array = [] + +@description('Optional. List of all routes.') +param routes array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-05-01' existing = { + name: virtualHubName +} + +resource hubRouteTable 'Microsoft.Network/virtualHubs/hubRouteTables@2022-05-01' = { + name: name + parent: virtualHub + properties: { + labels: !empty(labels) ? labels : null + routes: !empty(routes) ? routes : null + } +} + +@description('The name of the deployed virtual hub route table.') +output name string = hubRouteTable.name + +@description('The resource ID of the deployed virtual hub route table.') +output resourceId string = hubRouteTable.id + +@description('The resource group the virtual hub route table was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/readme.md new file mode 100644 index 000000000..5fd6130ee --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/readme.md @@ -0,0 +1,51 @@ +# Virtual Hub Route Table `[Microsoft.Network/virtualHubs/hubRouteTables]` + +This module deploys virtual hub route tables. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualHubs/hubRouteTables` | [2022-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/virtualHubs/hubRouteTables) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The route table name. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualHubName` | string | The name of the parent virtual hub. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `labels` | array | `[]` | List of labels associated with this route table. | +| `routes` | array | `[]` | List of all routes. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed virtual hub route table. | +| `resourceGroupName` | string | The resource group the virtual hub route table was deployed into. | +| `resourceId` | string | The resource ID of the deployed virtual hub route table. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubRouteTables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep new file mode 100644 index 000000000..f4a8276eb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/deploy.bicep @@ -0,0 +1,54 @@ +@description('Required. The connection name.') +param name string + +@description('Conditional. The name of the parent virtual hub. Required if the template is used in a standalone deployment.') +param virtualHubName string + +@description('Optional. Enable internet security.') +param enableInternetSecurity bool = true + +@description('Required. Resource ID of the virtual network to link to.') +param remoteVirtualNetworkId string + +@description('Optional. Routing Configuration indicating the associated and propagated route tables for this connection.') +param routingConfiguration object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-05-01' existing = { + name: virtualHubName +} + +resource hubVirtualNetworkConnection 'Microsoft.Network/virtualHubs/hubVirtualNetworkConnections@2022-05-01' = { + name: name + parent: virtualHub + properties: { + enableInternetSecurity: enableInternetSecurity + remoteVirtualNetwork: { + id: remoteVirtualNetworkId + } + routingConfiguration: !empty(routingConfiguration) ? routingConfiguration : null + } +} + +@description('The resource group the virtual hub connection was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the virtual hub connection.') +output resourceId string = hubVirtualNetworkConnection.id + +@description('The name of the virtual hub connection.') +output name string = hubVirtualNetworkConnection.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md new file mode 100644 index 000000000..d9a46f5ec --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/readme.md @@ -0,0 +1,56 @@ +# Virtual Hub Virtual Network Connections `[Microsoft.Network/virtualHubs/hubVirtualNetworkConnections]` + +This module deploys virtual hub virtual network connections. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2022-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/virtualHubs/hubVirtualNetworkConnections) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The connection name. | +| `remoteVirtualNetworkId` | string | Resource ID of the virtual network to link to. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualHubName` | string | The name of the parent virtual hub. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableInternetSecurity` | bool | `True` | Enable internet security. | +| `routingConfiguration` | object | `{object}` | Routing Configuration indicating the associated and propagated route tables for this connection. | + + +### Parameter Usage: `hubVirtualNetworkConnections` + +... + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the virtual hub connection. | +| `resourceGroupName` | string | The resource group the virtual hub connection was deployed into. | +| `resourceId` | string | The resource ID of the virtual hub connection. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/hubVirtualNetworkConnections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/readme.md new file mode 100644 index 000000000..675a7c887 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/readme.md @@ -0,0 +1,292 @@ +# Virtual Hubs `[Microsoft.Network/virtualHubs]` + +This module deploys a Virtual Hub. +If you are planning to deploy a Secure Virtual Hub (with an Azure Firewall integrated), please refer to the Azure Firewall module. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Network/virtualHubs` | [2022-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/virtualHubs) | +| `Microsoft.Network/virtualHubs/hubRouteTables` | [2022-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/virtualHubs/hubRouteTables) | +| `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2022-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-05-01/virtualHubs/hubVirtualNetworkConnections) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefix` | string | Address-prefix for this VirtualHub. | +| `name` | string | The virtual hub name. | +| `virtualWanId` | string | Resource ID of the virtual WAN to link to. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowBranchToBranchTraffic` | bool | `True` | | Flag to control transit for VirtualRouter hub. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `expressRouteGatewayId` | string | `''` | | Resource ID of the Express Route Gateway to link to. | +| `hubRouteTables` | _[hubRouteTables](hubRouteTables/readme.md)_ array | `[]` | | Route tables to create for the virtual hub. | +| `hubVirtualNetworkConnections` | _[hubVirtualNetworkConnections](hubVirtualNetworkConnections/readme.md)_ array | `[]` | | Virtual network connections to create for the virtual hub. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `p2SVpnGatewayId` | string | `''` | | Resource ID of the Point-to-Site VPN Gateway to link to. | +| `preferredRoutingGateway` | string | `''` | `['', ExpressRoute, None, VpnGateway]` | The preferred routing gateway types. | +| `routeTableRoutes` | array | `[]` | | VirtualHub route tables. | +| `securityPartnerProviderId` | string | `''` | | ID of the Security Partner Provider to link to. | +| `securityProviderName` | string | `''` | | The Security Provider name. | +| `sku` | string | `'Standard'` | `[Basic, Standard]` | The sku of this VirtualHub. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `virtualHubRouteTableV2s` | array | `[]` | | List of all virtual hub route table v2s associated with this VirtualHub. | +| `virtualRouterAsn` | int | `-1` | | VirtualRouter ASN. | +| `virtualRouterIps` | array | `[]` | | VirtualRouter IPs. | +| `vpnGatewayId` | string | `''` | | Resource ID of the VPN Gateway to link to. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual hub. | +| `resourceGroupName` | string | The resource group the virtual hub was deployed into. | +| `resourceId` | string | The resource ID of the virtual hub. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module virtualHubs './Microsoft.Network/virtualHubs/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvhcom' + params: { + // Required parameters + addressPrefix: '10.1.0.0/16' + name: '<>-nvhcom' + virtualWanId: '' + // Non-required parameters + enableDefaultTelemetry: '' + hubRouteTables: [ + { + name: 'routeTable1' + } + ] + hubVirtualNetworkConnections: [ + { + name: 'connection1' + remoteVirtualNetworkId: '' + routingConfiguration: { + associatedRouteTable: { + id: '' + } + propagatedRouteTables: { + ids: [ + { + id: '' + } + ] + labels: [ + 'none' + ] + } + } + } + ] + lock: 'CanNotDelete' + tags: { + Environment: 'Non-Prod' + 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 + "addressPrefix": { + "value": "10.1.0.0/16" + }, + "name": { + "value": "<>-nvhcom" + }, + "virtualWanId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "hubRouteTables": { + "value": [ + { + "name": "routeTable1" + } + ] + }, + "hubVirtualNetworkConnections": { + "value": [ + { + "name": "connection1", + "remoteVirtualNetworkId": "", + "routingConfiguration": { + "associatedRouteTable": { + "id": "" + }, + "propagatedRouteTables": { + "ids": [ + { + "id": "" + } + ], + "labels": [ + "none" + ] + } + } + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module virtualHubs './Microsoft.Network/virtualHubs/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvhmin' + params: { + // Required parameters + addressPrefix: '10.0.0.0/16' + name: '<>-nvhmin' + virtualWanId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "addressPrefix": { + "value": "10.0.0.0/16" + }, + "name": { + "value": "<>-nvhmin" + }, + "virtualWanId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualHubs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..f6a444107 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualNetworkGateway.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualNetworkGateway +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/dependencies.bicep new file mode 100644 index 000000000..49ffdf8cd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/dependencies.bicep @@ -0,0 +1,41 @@ +@description('Optional. The location to deploy resources 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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network.') +output vnetResourceId string = virtualNetwork.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/deploy.test.bicep new file mode 100644 index 000000000..f32c12e87 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/aadvpn/deploy.test.bicep @@ -0,0 +1,106 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvngavpn' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + activeActive: false + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + domainNameLabel: [ + '<>-dm-${serviceShort}' + ] + lock: 'CanNotDelete' + publicIpZones: [ + '1' + ] + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + vpnClientAadConfiguration: { + // The Application ID of the "Azure VPN" Azure AD Enterprise App for Azure Public + aadAudience: '41b23e61-6c1e-4545-b367-cd054e0ed4b4' + aadIssuer: 'https://sts.windows.net/${tenant().tenantId}/' + aadTenant: '${environment().authentication.loginEndpoint}/${tenant().tenantId}/' + vpnAuthenticationTypes: [ + 'AAD' + ] + vpnClientProtocols: [ + 'OpenVPN' + ] + } + vpnType: 'RouteBased' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/dependencies.bicep new file mode 100644 index 000000000..49ffdf8cd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/dependencies.bicep @@ -0,0 +1,41 @@ +@description('Optional. The location to deploy resources 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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Virtual Network.') +output vnetResourceId string = virtualNetwork.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/deploy.test.bicep new file mode 100644 index 000000000..4a2f8d9c9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/expressRoute/deploy.test.bicep @@ -0,0 +1,93 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvger' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + skuName: 'ErGw1AZ' + gatewayType: 'ExpressRoute' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + domainNameLabel: [ + '<>-dm-${serviceShort}' + ] + gatewayPipName: '<>-pip-${serviceShort}' + roleAssignments: [ + { + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Contact: 'test.user@testcompany.com' + CostCenter: '' + Environment: 'Validation' + PurchaseOrder: '' + Role: 'DeploymentValidation' + ServiceName: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/dependencies.bicep new file mode 100644 index 000000000..d1b8588e4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/dependencies.bicep @@ -0,0 +1,60 @@ +@description('Optional. The location to deploy resources 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 Local Network Gateway to create.') +param localNetworkGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2022-07-01' = { + name: localNetworkGatewayName + location: location + properties: { + gatewayIpAddress: '100.100.100.100' + localNetworkAddressSpace: { + addressPrefixes: [ + '192.168.0.0/24' + ] + } + } +} + +@description('The resource ID of the created Virtual Network.') +output vnetResourceId string = virtualNetwork.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Local Network Gateway.') +output localNetworkGatewayResourceId string = localNetworkGateway.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/deploy.test.bicep new file mode 100644 index 000000000..b7d294ed5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/.test/vpn/deploy.test.bicep @@ -0,0 +1,135 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvgvpn' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + localNetworkGatewayName: 'dep-<>-lng-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + vpnGatewayGeneration: 'Generation2' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + activeActive: true + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + domainNameLabel: [ + '<>-dm-${serviceShort}' + ] + lock: 'CanNotDelete' + publicIpZones: [ + '1' + ] + roleAssignments: [ + { + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + roleDefinitionIdOrName: 'Reader' + } + ] + vpnType: 'RouteBased' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: nestedDependencies.outputs.localNetworkGatewayResourceId + disableIPSecReplayProtection: true + allowRemoteVnetTraffic: true + natRules: [ + { + name: 'nat-rule-1-static-IngressSnat' + type: 'Static' + mode: 'IngressSnat' + internalMappings: [ + { + addressSpace: '10.100.0.0/24' + portRange: '100' + } + ] + externalMappings: [ + { + addressSpace: '192.168.0.0/24' + portRange: '100' + } + ] + } + { + name: 'nat-rule-2-dynamic-EgressSnat' + type: 'Dynamic' + mode: 'EgressSnat' + internalMappings: [ + { + addressSpace: '172.16.0.0/26' + } + ] + externalMappings: [ + { + addressSpace: '10.200.0.0/26' + } + ] + } + ] + enableBgpRouteTranslationForNat: true + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/deploy.bicep new file mode 100644 index 000000000..001a75fcd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/deploy.bicep @@ -0,0 +1,471 @@ +@description('Required. Specifies the Virtual Network Gateway name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Specifies the name of the Public IP used by the Virtual Network Gateway. If it\'s not provided, a \'-pip\' suffix will be appended to the gateway\'s name.') +param gatewayPipName string = '${name}-pip1' + +@description('Optional. Specifies the name of the Public IP used by the Virtual Network Gateway when active-active configuration is required. If it\'s not provided, a \'-pip\' suffix will be appended to the gateway\'s name.') +param activeGatewayPipName string = '${name}-pip2' + +@description('Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix.') +param publicIPPrefixResourceId string = '' + +@description('Optional. Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones.') +param publicIpZones array = [] + +@description('Optional. DNS name(s) of the Public IP resource(s). If you enabled active-active configuration, you need to provide 2 DNS names, if you want to use this feature. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com.') +param domainNameLabel array = [] + +@description('Required. Specifies the gateway type. E.g. VPN, ExpressRoute.') +@allowed([ + 'Vpn' + 'ExpressRoute' +]) +param gatewayType string + +@description('Optional. The generation for this VirtualNetworkGateway. Must be None if virtualNetworkGatewayType is not VPN.') +@allowed([ + 'Generation1' + 'Generation2' + 'None' +]) +param vpnGatewayGeneration string = 'None' + +@description('Required. The SKU of the Gateway.') +@allowed([ + 'Basic' + 'VpnGw1' + 'VpnGw2' + 'VpnGw3' + 'VpnGw4' + 'VpnGw5' + 'VpnGw1AZ' + 'VpnGw2AZ' + 'VpnGw3AZ' + 'VpnGw4AZ' + 'VpnGw5AZ' + 'Standard' + 'HighPerformance' + 'UltraPerformance' + 'ErGw1AZ' + 'ErGw2AZ' + 'ErGw3AZ' +]) +param skuName string + +@description('Optional. Specifies the VPN type.') +@allowed([ + 'PolicyBased' + 'RouteBased' +]) +param vpnType string = 'RouteBased' + +@description('Required. Virtual Network resource ID.') +param vNetResourceId string + +@description('Optional. Value to specify if the Gateway should be deployed in active-active or active-passive configuration.') +param activeActive bool = true + +@description('Optional. Value to specify if BGP is enabled or not.') +param enableBgp bool = true + +@description('Optional. ASN value.') +param asn int = 65815 + +@description('Optional. The IP address range from which VPN clients will receive an IP address when connected. Range specified must not overlap with on-premise network.') +param vpnClientAddressPoolPrefix string = '' + +@description('Optional. Configures this gateway to accept traffic from remote Virtual WAN networks.') +param allowVirtualWanTraffic bool = false + +@description('Optional. Configure this gateway to accept traffic from other Azure Virtual Networks. This configuration does not support connectivity to Azure Virtual WAN.') +param allowRemoteVnetTraffic bool = false + +@description('Optional. disableIPSecReplayProtection flag. Used for VPN Gateways.') +param disableIPSecReplayProtection bool = false + +@description('Optional. Whether DNS forwarding is enabled or not and is only supported for Express Route Gateways. The DNS forwarding feature flag must be enabled on the current subscription.') +param enableDnsForwarding bool = false + +@description('Optional. Whether private IP needs to be enabled on this gateway for connections or not. Used for configuring a Site-to-Site VPN connection over ExpressRoute private peering.') +param enablePrivateIpAddress bool = false + +@description('Optional. The reference to the LocalNetworkGateway resource which represents local network site having default routes. Assign Null value in case of removing existing default site setting.') +param gatewayDefaultSiteLocalNetworkGatewayId string = '' + +@description('Optional. NatRules for virtual network gateway. NAT is supported on the the following SKUs: VpnGw2~5, VpnGw2AZ~5AZ and is supported for IPsec/IKE cross-premises connections only.') +param natRules array = [] + +@description('Optional. EnableBgpRouteTranslationForNat flag. Can only be used when "natRules" are enabled on the Virtual Network Gateway.') +param enableBgpRouteTranslationForNat bool = false + +@description('Optional. Client root certificate data used to authenticate VPN clients. Cannot be configured if vpnClientAadConfiguration is provided.') +param clientRootCertData string = '' + +@description('Optional. Thumbprint of the revoked certificate. This would revoke VPN client certificates matching this thumbprint from connecting to the VNet.') +param clientRevokedCertThumbprint string = '' + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'DDoSProtectionNotifications' + 'DDoSMitigationFlowLogs' + 'DDoSMitigationReports' +]) +param publicIpdiagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'GatewayDiagnosticLog' + 'TunnelDiagnosticLog' + 'RouteDiagnosticLog' + 'IKEDiagnosticLog' + 'P2SDiagnosticLog' +]) +param virtualNetworkGatewaydiagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. Configuration for AAD Authentication for P2S Tunnel Type, Cannot be configured if clientRootCertData is provided.') +param vpnClientAadConfiguration object = {} + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +@description('Optional. The name of the public IP diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param publicIpDiagnosticSettingsName string = '' + +// ================// +// Variables // +// ================// + +// Diagnostic Variables +var virtualNetworkGatewayDiagnosticsLogsSpecified = [for category in filter(virtualNetworkGatewaydiagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var virtualNetworkGatewayDiagnosticsLogs = contains(virtualNetworkGatewaydiagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : virtualNetworkGatewayDiagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +// Other Variables +var zoneRedundantSkus = [ + 'VpnGw1AZ' + 'VpnGw2AZ' + 'VpnGw3AZ' + 'VpnGw4AZ' + 'VpnGw5AZ' + 'ErGw1AZ' + 'ErGw2AZ' + 'ErGw3AZ' +] +var gatewayPipSku = contains(zoneRedundantSkus, skuName) ? 'Standard' : 'Basic' +var gatewayPipAllocationMethod = contains(zoneRedundantSkus, skuName) ? 'Static' : 'Dynamic' + +var isActiveActiveValid = gatewayType != 'ExpressRoute' ? activeActive : false +var virtualGatewayPipNameVar = isActiveActiveValid ? [ + gatewayPipName + activeGatewayPipName +] : [ + gatewayPipName +] + +var vpnTypeVar = gatewayType != 'ExpressRoute' ? vpnType : 'PolicyBased' + +var isBgpValid = gatewayType != 'ExpressRoute' ? enableBgp : false +var bgpSettings = { + asn: asn +} + +// Potential configurations (active-active vs active-passive) +var ipConfiguration = isActiveActiveValid ? [ + { + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '${vNetResourceId}/subnets/GatewaySubnet' + } + publicIPAddress: { + id: az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) + } + } + name: 'vNetGatewayConfig1' + } + { + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '${vNetResourceId}/subnets/GatewaySubnet' + } + publicIPAddress: { + id: isActiveActiveValid ? az.resourceId('Microsoft.Network/publicIPAddresses', activeGatewayPipName) : az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) + } + } + name: 'vNetGatewayConfig2' + } +] : [ + { + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: '${vNetResourceId}/subnets/GatewaySubnet' + } + publicIPAddress: { + id: az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) + } + } + name: 'vNetGatewayConfig1' + } +] + +var vpnClientConfiguration = !empty(clientRootCertData) ? { + vpnClientAddressPool: { + addressPrefixes: [ + vpnClientAddressPoolPrefix + ] + } + vpnClientRootCertificates: [ + { + name: 'RootCert1' + properties: { + PublicCertData: clientRootCertData + } + } + ] + vpnClientRevokedCertificates: !empty(clientRevokedCertThumbprint) ? [ + { + name: 'RevokedCert1' + properties: { + Thumbprint: clientRevokedCertThumbprint + } + } + ] : null +} : !empty(vpnClientAadConfiguration) ? { + vpnClientAddressPool: { + addressPrefixes: [ + vpnClientAddressPoolPrefix + ] + } + aadTenant: vpnClientAadConfiguration.aadTenant + aadAudience: vpnClientAadConfiguration.aadAudience + aadIssuer: vpnClientAadConfiguration.aadIssuer + vpnAuthenticationTypes: [ + vpnClientAadConfiguration.vpnAuthenticationTypes + ] + vpnClientProtocols: [ + vpnClientAadConfiguration.vpnClientProtocols + ] +} : null + +var enableReferencedModulesTelemetry = false + +// ================// +// Deployments // +// ================// +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +// Public IPs +@batchSize(1) +module publicIPAddress '../publicIPAddresses/deploy.bicep' = [for (virtualGatewayPublicIpName, index) in virtualGatewayPipNameVar: { + name: virtualGatewayPublicIpName + params: { + name: virtualGatewayPublicIpName + diagnosticLogCategoriesToEnable: publicIpdiagnosticLogCategoriesToEnable + diagnosticMetricsToEnable: diagnosticMetricsToEnable + diagnosticSettingsName: !empty(publicIpDiagnosticSettingsName) ? publicIpDiagnosticSettingsName : '${virtualGatewayPublicIpName}-diagnosticSettings' + diagnosticStorageAccountId: diagnosticStorageAccountId + diagnosticWorkspaceId: diagnosticWorkspaceId + diagnosticEventHubAuthorizationRuleId: diagnosticEventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticEventHubName + domainNameLabel: length(virtualGatewayPipNameVar) == length(domainNameLabel) ? domainNameLabel[index] : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + location: location + lock: lock + publicIPAllocationMethod: gatewayPipAllocationMethod + publicIPPrefixResourceId: !empty(publicIPPrefixResourceId) ? publicIPPrefixResourceId : '' + tags: tags + skuName: gatewayPipSku + zones: contains(zoneRedundantSkus, skuName) ? publicIpZones : [] + } +}] + +// VNET Gateway +// ============ +resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + ipConfigurations: ipConfiguration + activeActive: isActiveActiveValid + allowRemoteVnetTraffic: allowRemoteVnetTraffic + allowVirtualWanTraffic: allowVirtualWanTraffic + enableBgp: isBgpValid + bgpSettings: isBgpValid ? bgpSettings : null + disableIPSecReplayProtection: disableIPSecReplayProtection + enableDnsForwarding: gatewayType == 'ExpressRoute' ? enableDnsForwarding : null + enablePrivateIpAddress: enablePrivateIpAddress + enableBgpRouteTranslationForNat: enableBgpRouteTranslationForNat + gatewayType: gatewayType + gatewayDefaultSite: !empty(gatewayDefaultSiteLocalNetworkGatewayId) ? { + id: gatewayDefaultSiteLocalNetworkGatewayId + } : null + sku: { + name: skuName + tier: skuName + } + vpnType: vpnTypeVar + vpnClientConfiguration: !empty(vpnClientAddressPoolPrefix) ? vpnClientConfiguration : null + vpnGatewayGeneration: gatewayType == 'Vpn' ? vpnGatewayGeneration : 'None' + } + dependsOn: [ + publicIPAddress + ] +} + +module virtualNetworkGateway_natRules 'natRules/deploy.bicep' = [for (natRule, index) in natRules: { + name: '${deployment().name}-NATRule-${index}' + params: { + name: natRule.name + virtualNetworkGatewayName: virtualNetworkGateway.name + externalMappings: contains(natRule, 'externalMappings') ? natRule.externalMappings : [] + internalMappings: contains(natRule, 'internalMappings') ? natRule.internalMappings : [] + ipConfigurationId: contains(natRule, 'ipConfigurationId') ? natRule.ipConfigurationId : '' + mode: contains(natRule, 'mode') ? natRule.mode : '' + type: contains(natRule, 'type') ? natRule.type : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource virtualNetworkGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${virtualNetworkGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualNetworkGateway +} + +resource virtualNetworkGateway_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: virtualNetworkGatewayDiagnosticsLogs + } + scope: virtualNetworkGateway +} + +module virtualNetworkGateway_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VNetGateway-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: virtualNetworkGateway.id + } +}] + +// ================// +// Outputs // +// ================// +@description('The resource group the virtual network gateway was deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual network gateway.') +output name string = virtualNetworkGateway.name + +@description('The resource ID of the virtual network gateway.') +output resourceId string = virtualNetworkGateway.id + +@description('Shows if the virtual network gateway is configured in active-active mode.') +output activeActive bool = virtualNetworkGateway.properties.activeActive + +@description('The location the resource was deployed into.') +output location string = virtualNetworkGateway.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/deploy.bicep new file mode 100644 index 000000000..d0c3eab4f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/deploy.bicep @@ -0,0 +1,70 @@ +@description('Required. The name of the NAT rule.') +param name string + +@description('Conditional. The name of the parent Virtual Network Gateway this NAT rule is associated with. Required if the template is used in a standalone deployment.') +param virtualNetworkGatewayName string + +@description('Optional. An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range.') +param externalMappings array = [] + +@description('Optional. An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range.') +param internalMappings array = [] + +@description('Optional. A NAT rule must be configured to a specific Virtual Network Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both Virtual Network Gateway instances.') +param ipConfigurationId string = '' + +@description('Optional. The type of NAT rule for Virtual Network NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub\'s site-to-site Virtual Network gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub\'s Site-to-site Virtual Network gateway.') +@allowed([ + '' + 'EgressSnat' + 'IngressSnat' +]) +param mode string = '' + +@description('Optional. The type of NAT rule for Virtual Network NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability.') +@allowed([ + '' + 'Dynamic' + 'Static' +]) +param type string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2022-07-01' existing = { + name: virtualNetworkGatewayName +} + +resource natRule 'Microsoft.Network/virtualNetworkGateways/natRules@2022-07-01' = { + name: name + parent: virtualNetworkGateway + properties: { + externalMappings: externalMappings + internalMappings: internalMappings + ipConfigurationId: !empty(ipConfigurationId) ? ipConfigurationId : null + mode: !empty(mode) ? any(mode) : null + type: !empty(type) ? any(type) : null + } +} + +@description('The name of the NAT rule.') +output name string = natRule.name + +@description('The resource ID of the NAT rule.') +output resourceId string = natRule.id + +@description('The name of the resource group the NAT rule was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/readme.md new file mode 100644 index 000000000..ac29e736e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/readme.md @@ -0,0 +1,54 @@ +# VPN Gateways NATRules `[Microsoft.Network/virtualNetworkGateways/natRules]` + +This module deploys Virtual Network Gateways NATRules + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualNetworkGateways/natRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworkGateways/natRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NAT rule. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualNetworkGatewayName` | string | The name of the parent Virtual Network Gateway this NAT rule is associated with. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `externalMappings` | array | `[]` | | An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range. | +| `internalMappings` | array | `[]` | | An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range. | +| `ipConfigurationId` | string | `''` | | A NAT rule must be configured to a specific Virtual Network Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both Virtual Network Gateway instances. | +| `mode` | string | `''` | `['', EgressSnat, IngressSnat]` | The type of NAT rule for Virtual Network NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site Virtual Network gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site Virtual Network gateway. | +| `type` | string | `''` | `['', Dynamic, Static]` | The type of NAT rule for Virtual Network NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NAT rule. | +| `resourceGroupName` | string | The name of the resource group the NAT rule was deployed into. | +| `resourceId` | string | The resource ID of the NAT rule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/natRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/readme.md new file mode 100644 index 000000000..4e8794e59 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/readme.md @@ -0,0 +1,775 @@ +# Virtual Network Gateways `[Microsoft.Network/virtualNetworkGateways]` + +This module deploys a virtual network gateway. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/publicIPAddresses` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/publicIPAddresses) | +| `Microsoft.Network/virtualNetworkGateways` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworkGateways) | +| `Microsoft.Network/virtualNetworkGateways/natRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworkGateways/natRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `gatewayType` | string | `[ExpressRoute, Vpn]` | Specifies the gateway type. E.g. VPN, ExpressRoute. | +| `name` | string | | Specifies the Virtual Network Gateway name. | +| `skuName` | string | `[Basic, ErGw1AZ, ErGw2AZ, ErGw3AZ, HighPerformance, Standard, UltraPerformance, VpnGw1, VpnGw1AZ, VpnGw2, VpnGw2AZ, VpnGw3, VpnGw3AZ, VpnGw4, VpnGw4AZ, VpnGw5, VpnGw5AZ]` | The SKU of the Gateway. | +| `vNetResourceId` | string | | Virtual Network resource ID. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `activeActive` | bool | `True` | | Value to specify if the Gateway should be deployed in active-active or active-passive configuration. | +| `activeGatewayPipName` | string | `[format('{0}-pip2', parameters('name'))]` | | Specifies the name of the Public IP used by the Virtual Network Gateway when active-active configuration is required. If it's not provided, a '-pip' suffix will be appended to the gateway's name. | +| `allowRemoteVnetTraffic` | bool | `False` | | Configure this gateway to accept traffic from other Azure Virtual Networks. This configuration does not support connectivity to Azure Virtual WAN. | +| `allowVirtualWanTraffic` | bool | `False` | | Configures this gateway to accept traffic from remote Virtual WAN networks. | +| `asn` | int | `65815` | | ASN value. | +| `clientRevokedCertThumbprint` | string | `''` | | Thumbprint of the revoked certificate. This would revoke VPN client certificates matching this thumbprint from connecting to the VNet. | +| `clientRootCertData` | string | `''` | | Client root certificate data used to authenticate VPN clients. Cannot be configured if vpnClientAadConfiguration is provided. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `disableIPSecReplayProtection` | bool | `False` | | disableIPSecReplayProtection flag. Used for VPN Gateways. | +| `domainNameLabel` | array | `[]` | | DNS name(s) of the Public IP resource(s). If you enabled active-active configuration, you need to provide 2 DNS names, if you want to use this feature. A region specific suffix will be appended to it, e.g.: your-DNS-name.westeurope.cloudapp.azure.com. | +| `enableBgp` | bool | `True` | | Value to specify if BGP is enabled or not. | +| `enableBgpRouteTranslationForNat` | bool | `False` | | EnableBgpRouteTranslationForNat flag. Can only be used when "natRules" are enabled on the Virtual Network Gateway. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableDnsForwarding` | bool | `False` | | Whether DNS forwarding is enabled or not and is only supported for Express Route Gateways. The DNS forwarding feature flag must be enabled on the current subscription. | +| `enablePrivateIpAddress` | bool | `False` | | Whether private IP needs to be enabled on this gateway for connections or not. Used for configuring a Site-to-Site VPN connection over ExpressRoute private peering. | +| `gatewayDefaultSiteLocalNetworkGatewayId` | string | `''` | | The reference to the LocalNetworkGateway resource which represents local network site having default routes. Assign Null value in case of removing existing default site setting. | +| `gatewayPipName` | string | `[format('{0}-pip1', parameters('name'))]` | | Specifies the name of the Public IP used by the Virtual Network Gateway. If it's not provided, a '-pip' suffix will be appended to the gateway's name. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natRules` | _[natRules](natRules/readme.md)_ array | `[]` | | NatRules for virtual network gateway. NAT is supported on the the following SKUs: VpnGw2~5, VpnGw2AZ~5AZ and is supported for IPsec/IKE cross-premises connections only. | +| `publicIpdiagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, DDoSMitigationFlowLogs, DDoSMitigationReports, DDoSProtectionNotifications]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `publicIpDiagnosticSettingsName` | string | `''` | | The name of the public IP diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `publicIPPrefixResourceId` | string | `''` | | Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix. | +| `publicIpZones` | array | `[]` | | Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `virtualNetworkGatewaydiagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, GatewayDiagnosticLog, IKEDiagnosticLog, P2SDiagnosticLog, RouteDiagnosticLog, TunnelDiagnosticLog]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `vpnClientAadConfiguration` | object | `{object}` | | Configuration for AAD Authentication for P2S Tunnel Type, Cannot be configured if clientRootCertData is provided. | +| `vpnClientAddressPoolPrefix` | string | `''` | | The IP address range from which VPN clients will receive an IP address when connected. Range specified must not overlap with on-premise network. | +| `vpnGatewayGeneration` | string | `'None'` | `[Generation1, Generation2, None]` | The generation for this VirtualNetworkGateway. Must be None if virtualNetworkGatewayType is not VPN. | +| `vpnType` | string | `'RouteBased'` | `[PolicyBased, RouteBased]` | Specifies the VPN type. | + + +### Parameter Usage: `subnets` + +The `subnets` parameter accepts a JSON Array of `subnet` objects to deploy to the Virtual Network. + +Here's an example of specifying a couple Subnets to deploy: + +

+ +Parameter JSON format + +```json +"subnets": { + "value": [ + { + "name": "app", + "properties": { + "addressPrefix": "10.1.0.0/24", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', 'app-nsg')]" + }, + "routeTable": { + "id": "[resourceId('Microsoft.Network/routeTables', 'app-udr')]" + } + } + }, + { + "name": "data", + "properties": { + "addressPrefix": "10.1.1.0/24" + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +subnets: [ + { + name: 'app' + properties: { + addressPrefix: '10.1.0.0/24' + networkSecurityGroup: { + id: '[resourceId('Microsoft.Network/networkSecurityGroups' 'app-nsg')]' + } + routeTable: { + id: '[resourceId('Microsoft.Network/routeTables' 'app-udr')]' + } + } + } + { + name: 'data' + properties: { + addressPrefix: '10.1.1.0/24' + } + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `activeActive` | bool | Shows if the virtual network gateway is configured in active-active mode. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual network gateway. | +| `resourceGroupName` | string | The resource group the virtual network gateway was deployed. | +| `resourceId` | string | The resource ID of the virtual network gateway. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Network/publicIPAddresses` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Aadvpn

+ +
+ +via Bicep module + +```bicep +module virtualNetworkGateways './Microsoft.Network/virtualNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvngavpn' + params: { + // Required parameters + gatewayType: 'Vpn' + name: '<>nvngavpn001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + activeActive: false + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + domainNameLabel: [ + '<>-dm-nvngavpn' + ] + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + publicIpZones: [ + '1' + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vpnClientAadConfiguration: { + aadAudience: '41b23e61-6c1e-4545-b367-cd054e0ed4b4' + aadIssuer: '' + aadTenant: '' + vpnAuthenticationTypes: [ + 'AAD' + ] + vpnClientProtocols: [ + 'OpenVPN' + ] + } + vpnType: 'RouteBased' + } +} +``` + +
+

+ +

+ +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 + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "<>nvngavpn001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "activeActive": { + "value": false + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "domainNameLabel": { + "value": [ + "<>-dm-nvngavpn" + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "publicIpZones": { + "value": [ + "1" + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "vpnClientAadConfiguration": { + "value": { + "aadAudience": "41b23e61-6c1e-4545-b367-cd054e0ed4b4", + "aadIssuer": "", + "aadTenant": "", + "vpnAuthenticationTypes": [ + "AAD" + ], + "vpnClientProtocols": [ + "OpenVPN" + ] + } + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

+ +

Example 2: Expressroute

+ +
+ +via Bicep module + +```bicep +module virtualNetworkGateways './Microsoft.Network/virtualNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvger' + params: { + // Required parameters + gatewayType: 'ExpressRoute' + name: '<>nvger001' + skuName: 'ErGw1AZ' + vNetResourceId: '' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + domainNameLabel: [ + '<>-dm-nvger' + ] + enableDefaultTelemetry: '' + gatewayPipName: '<>-pip-nvger' + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Contact: 'test.user@testcompany.com' + CostCenter: '' + Environment: 'Validation' + PurchaseOrder: '' + Role: 'DeploymentValidation' + ServiceName: '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 + "gatewayType": { + "value": "ExpressRoute" + }, + "name": { + "value": "<>nvger001" + }, + "skuName": { + "value": "ErGw1AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "domainNameLabel": { + "value": [ + "<>-dm-nvger" + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "gatewayPipName": { + "value": "<>-pip-nvger" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Contact": "test.user@testcompany.com", + "CostCenter": "", + "Environment": "Validation", + "PurchaseOrder": "", + "Role": "DeploymentValidation", + "ServiceName": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 3: Vpn

+ +
+ +via Bicep module + +```bicep +module virtualNetworkGateways './Microsoft.Network/virtualNetworkGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvgvpn' + params: { + // Required parameters + gatewayType: 'Vpn' + name: '<>nvgvpn001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + activeActive: true + allowRemoteVnetTraffic: true + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + disableIPSecReplayProtection: true + domainNameLabel: [ + '<>-dm-nvgvpn' + ] + enableBgpRouteTranslationForNat: true + enableDefaultTelemetry: '' + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: '' + lock: 'CanNotDelete' + natRules: [ + { + externalMappings: [ + { + addressSpace: '192.168.0.0/24' + portRange: '100' + } + ] + internalMappings: [ + { + addressSpace: '10.100.0.0/24' + portRange: '100' + } + ] + mode: 'IngressSnat' + name: 'nat-rule-1-static-IngressSnat' + type: 'Static' + } + { + externalMappings: [ + { + addressSpace: '10.200.0.0/26' + } + ] + internalMappings: [ + { + addressSpace: '172.16.0.0/26' + } + ] + mode: 'EgressSnat' + name: 'nat-rule-2-dynamic-EgressSnat' + type: 'Dynamic' + } + ] + publicIpZones: [ + '1' + ] + roleAssignments: [ + { + principalIds: [ + '' + ] + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vpnGatewayGeneration: 'Generation2' + vpnType: 'RouteBased' + } +} +``` + +
+

+ +

+ +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 + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "<>nvgvpn001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "activeActive": { + "value": true + }, + "allowRemoteVnetTraffic": { + "value": true + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "disableIPSecReplayProtection": { + "value": true + }, + "domainNameLabel": { + "value": [ + "<>-dm-nvgvpn" + ] + }, + "enableBgpRouteTranslationForNat": { + "value": true + }, + "enableDefaultTelemetry": { + "value": "" + }, + "enablePrivateIpAddress": { + "value": true + }, + "gatewayDefaultSiteLocalNetworkGatewayId": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "natRules": { + "value": [ + { + "externalMappings": [ + { + "addressSpace": "192.168.0.0/24", + "portRange": "100" + } + ], + "internalMappings": [ + { + "addressSpace": "10.100.0.0/24", + "portRange": "100" + } + ], + "mode": "IngressSnat", + "name": "nat-rule-1-static-IngressSnat", + "type": "Static" + }, + { + "externalMappings": [ + { + "addressSpace": "10.200.0.0/26" + } + ], + "internalMappings": [ + { + "addressSpace": "172.16.0.0/26" + } + ], + "mode": "EgressSnat", + "name": "nat-rule-2-dynamic-EgressSnat", + "type": "Dynamic" + } + ] + }, + "publicIpZones": { + "value": [ + "1" + ] + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "vpnGatewayGeneration": { + "value": "Generation2" + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworkGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..c9f8304b8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualNetwork.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualNetwork +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep new file mode 100644 index 000000000..a2fb172d4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/dependencies.bicep @@ -0,0 +1,35 @@ +@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 Route Table to create.') +param routeTableName string + +@description('Required. The name of the Network Security Group to create.') +param networkSecurityGroupName string + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource routeTable 'Microsoft.Network/routeTables@2022-01-01' = { + name: routeTableName + location: location +} + +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-01-01' = { + name: networkSecurityGroupName + location: location +} + +@description('The resource ID of the created Route Table.') +output routeTableResourceId string = routeTable.id + +@description('The resource ID of the created Network Security Group.') +output networkSecurityGroupResourceId string = networkSecurityGroup.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep new file mode 100644 index 000000000..926ec4f7c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/common/deploy.test.bicep @@ -0,0 +1,140 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvncom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + routeTableName: 'dep-<>-rt-${serviceShort}' + networkSecurityGroupName: 'dep-<>-nsg-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + addressPrefixes: [ + '10.0.0.0/16' + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + dnsServers: [ + '10.0.1.4' + '10.0.1.5' + ] + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + flowTimeoutInMinutes: 20 + subnets: [ + { + addressPrefix: '10.0.255.0/24' + name: 'GatewaySubnet' + } + { + addressPrefix: '10.0.0.0/24' + name: '<>-az-subnet-x-001' + networkSecurityGroupId: nestedDependencies.outputs.networkSecurityGroupResourceId + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + routeTableId: nestedDependencies.outputs.routeTableResourceId + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.Sql' + } + ] + } + { + addressPrefix: '10.0.3.0/24' + delegations: [ + { + name: 'netappDel' + properties: { + serviceName: 'Microsoft.Netapp/volumes' + } + } + ] + name: '<>-az-subnet-x-002' + } + { + addressPrefix: '10.0.6.0/24' + name: '<>-az-subnet-x-003' + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + ] + tags:{ + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep new file mode 100644 index 000000000..7c156706c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/min/deploy.test.bicep @@ -0,0 +1,45 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvnmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + addressPrefixes: [ + '10.0.0.0/16' + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep new file mode 100644 index 000000000..249436cb0 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/dependencies.bicep @@ -0,0 +1,30 @@ +@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 + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: addressPrefix + } + } + ] + } +} + +@description('The resource ID of the created Virtual Network.') +output virtualNetworkResourceId string = virtualNetwork.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep new file mode 100644 index 000000000..b8336b874 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/.test/vnetPeering/deploy.test.bicep @@ -0,0 +1,76 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualnetworks-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvnpeer' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualNetworkName: 'dep-<>-vnet-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + addressPrefixes: [ + '10.1.0.0/24' + ] + subnets: [ + { + addressPrefix: '10.1.0.0/26' + name: 'GatewaySubnet' + } + ] + peerings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringEnabled: true + remotePeeringName: 'customName' + remoteVirtualNetworkId: nestedDependencies.outputs.virtualNetworkResourceId + useRemoteGateways: false + } + ] + tags:{ + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/deploy.bicep new file mode 100644 index 000000000..fcc2c7030 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/deploy.bicep @@ -0,0 +1,302 @@ +@description('Required. The Virtual Network (vNet) Name.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. An Array of 1 or more IP Address Prefixes for the Virtual Network.') +param addressPrefixes array + +@description('Optional. An Array of subnets to deploy to the Virtual Network.') +param subnets array = [] + +@description('Optional. DNS Servers associated to the Virtual Network.') +param dnsServers array = [] + +@description('Optional. Resource ID of the DDoS protection plan to assign the VNET to. If it\'s left blank, DDoS protection will not be configured. If it\'s provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription.') +param ddosProtectionPlanId string = '' + +@description('Optional. Virtual Network Peerings configurations.') +param peerings array = [] + +@description('Optional. Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property.') +param vnetEncryption bool = false + +@allowed([ + 'AllowUnencrypted' + 'DropUnencrypted' +]) +@description('Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled.') +param vnetEncryptionEnforcement string = 'AllowUnencrypted' + +@maxValue(30) +@description('Optional. The flow timeout in minutes for the Virtual Network, which is used to enable connection tracking for intra-VM flows. Possible values are between 4 and 30 minutes. Default value 0 will set the property to null.') +param flowTimeoutInMinutes int = 0 + +// @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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of the diagnostic log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + '' + 'allLogs' + 'VMProtectionAlerts' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs' && item != ''): { + category: category + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } + } +] : contains(diagnosticLogCategoriesToEnable, '') ? [] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + // retentionPolicy: { + // enabled: true + // days: diagnosticLogsRetentionInDays + // } +}] + +var dnsServersVar = { + dnsServers: array(dnsServers) +} + +var ddosProtectionPlan = { + id: ddosProtectionPlanId +} + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + addressSpace: { + addressPrefixes: addressPrefixes + } + ddosProtectionPlan: !empty(ddosProtectionPlanId) ? ddosProtectionPlan : null + dhcpOptions: !empty(dnsServers) ? dnsServersVar : null + enableDdosProtection: !empty(ddosProtectionPlanId) + encryption: vnetEncryption == true ? { + enabled: vnetEncryption + enforcement: vnetEncryptionEnforcement + } : null + flowTimeoutInMinutes: flowTimeoutInMinutes != 0 ? flowTimeoutInMinutes : null + subnets: [for subnet in subnets: { + name: subnet.name + properties: { + addressPrefix: subnet.addressPrefix + addressPrefixes: contains(subnet, 'addressPrefixes') ? subnet.addressPrefixes : [] + applicationGatewayIpConfigurations: contains(subnet, 'applicationGatewayIpConfigurations') ? subnet.applicationGatewayIpConfigurations : [] + delegations: contains(subnet, 'delegations') ? subnet.delegations : [] + ipAllocations: contains(subnet, 'ipAllocations') ? subnet.ipAllocations : [] + natGateway: contains(subnet, 'natGatewayId') ? { + id: subnet.natGatewayId + } : null + networkSecurityGroup: contains(subnet, 'networkSecurityGroupId') ? { + id: subnet.networkSecurityGroupId + } : null + privateEndpointNetworkPolicies: contains(subnet, 'privateEndpointNetworkPolicies') ? subnet.privateEndpointNetworkPolicies : null + privateLinkServiceNetworkPolicies: contains(subnet, 'privateLinkServiceNetworkPolicies') ? subnet.privateLinkServiceNetworkPolicies : null + routeTable: contains(subnet, 'routeTableId') ? { + id: subnet.routeTableId + } : null + serviceEndpoints: contains(subnet, 'serviceEndpoints') ? subnet.serviceEndpoints : [] + serviceEndpointPolicies: contains(subnet, 'serviceEndpointPolicies') ? subnet.serviceEndpointPolicies : [] + } + }] + } +} + +//NOTE Start: ------------------------------------ +// The below module (virtualNetwork_subnets) is a duplicate of the child resource (subnets) defined in the parent module (virtualNetwork). +// The reason it exists so that deployment validation tests can be performed on the child module (subnets), in case that module needed to be deployed alone outside of this template. +// The reason for duplication is due to the current design for the (virtualNetworks) resource from Azure, where if the child module (subnets) does not exist within it, causes +// an issue, where the child resource (subnets) gets all of its properties removed, hence not as 'idempotent' as it should be. See https://github.com/Azure/azure-quickstart-templates/issues/2786 for more details. +// You can safely remove the below child module (virtualNetwork_subnets) in your consumption of the module (virtualNetworks) to reduce the template size and duplication. +//NOTE End : ------------------------------------ + +module virtualNetwork_subnets 'subnets/deploy.bicep' = [for (subnet, index) in subnets: { + name: '${uniqueString(deployment().name, location)}-subnet-${index}' + params: { + virtualNetworkName: virtualNetwork.name + name: subnet.name + addressPrefix: subnet.addressPrefix + addressPrefixes: contains(subnet, 'addressPrefixes') ? subnet.addressPrefixes : [] + applicationGatewayIpConfigurations: contains(subnet, 'applicationGatewayIpConfigurations') ? subnet.applicationGatewayIpConfigurations : [] + delegations: contains(subnet, 'delegations') ? subnet.delegations : [] + ipAllocations: contains(subnet, 'ipAllocations') ? subnet.ipAllocations : [] + natGatewayId: contains(subnet, 'natGatewayId') ? subnet.natGatewayId : '' + networkSecurityGroupId: contains(subnet, 'networkSecurityGroupId') ? subnet.networkSecurityGroupId : '' + privateEndpointNetworkPolicies: contains(subnet, 'privateEndpointNetworkPolicies') ? subnet.privateEndpointNetworkPolicies : '' + privateLinkServiceNetworkPolicies: contains(subnet, 'privateLinkServiceNetworkPolicies') ? subnet.privateLinkServiceNetworkPolicies : '' + roleAssignments: contains(subnet, 'roleAssignments') ? subnet.roleAssignments : [] + routeTableId: contains(subnet, 'routeTableId') ? subnet.routeTableId : '' + serviceEndpointPolicies: contains(subnet, 'serviceEndpointPolicies') ? subnet.serviceEndpointPolicies : [] + serviceEndpoints: contains(subnet, 'serviceEndpoints') ? subnet.serviceEndpoints : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +// Local to Remote peering +module virtualNetwork_peering_local 'virtualNetworkPeerings/deploy.bicep' = [for (peering, index) in peerings: { + name: '${uniqueString(deployment().name, location)}-virtualNetworkPeering-local-${index}' + params: { + localVnetName: virtualNetwork.name + remoteVirtualNetworkId: peering.remoteVirtualNetworkId + name: contains(peering, 'name') ? peering.name : '${name}-${last(split(peering.remoteVirtualNetworkId, '/'))}' + allowForwardedTraffic: contains(peering, 'allowForwardedTraffic') ? peering.allowForwardedTraffic : true + allowGatewayTransit: contains(peering, 'allowGatewayTransit') ? peering.allowGatewayTransit : false + allowVirtualNetworkAccess: contains(peering, 'allowVirtualNetworkAccess') ? peering.allowVirtualNetworkAccess : true + doNotVerifyRemoteGateways: contains(peering, 'doNotVerifyRemoteGateways') ? peering.doNotVerifyRemoteGateways : true + useRemoteGateways: contains(peering, 'useRemoteGateways') ? peering.useRemoteGateways : false + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +// Remote to local peering (reverse) +module virtualNetwork_peering_remote 'virtualNetworkPeerings/deploy.bicep' = [for (peering, index) in peerings: if (contains(peering, 'remotePeeringEnabled') ? peering.remotePeeringEnabled == true : false) { + name: '${uniqueString(deployment().name, location)}-virtualNetworkPeering-remote-${index}' + scope: resourceGroup(split(peering.remoteVirtualNetworkId, '/')[2], split(peering.remoteVirtualNetworkId, '/')[4]) + params: { + localVnetName: last(split(peering.remoteVirtualNetworkId, '/'))! + remoteVirtualNetworkId: virtualNetwork.id + name: contains(peering, 'remotePeeringName') ? peering.remotePeeringName : '${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}' + allowForwardedTraffic: contains(peering, 'remotePeeringAllowForwardedTraffic') ? peering.remotePeeringAllowForwardedTraffic : true + allowGatewayTransit: contains(peering, 'remotePeeringAllowGatewayTransit') ? peering.remotePeeringAllowGatewayTransit : false + allowVirtualNetworkAccess: contains(peering, 'remotePeeringAllowVirtualNetworkAccess') ? peering.remotePeeringAllowVirtualNetworkAccess : true + doNotVerifyRemoteGateways: contains(peering, 'remotePeeringDoNotVerifyRemoteGateways') ? peering.remotePeeringDoNotVerifyRemoteGateways : true + useRemoteGateways: contains(peering, 'remotePeeringUseRemoteGateways') ? peering.remotePeeringUseRemoteGateways : false + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource virtualNetwork_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${virtualNetwork.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualNetwork +} + +resource virtualNetwork_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(diagnosticStorageAccountId) || !empty(diagnosticWorkspaceId) || !empty(diagnosticEventHubAuthorizationRuleId) || !empty(diagnosticEventHubName)) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: virtualNetwork +} + +module virtualNetwork_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VNet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: virtualNetwork.id + } +}] + +@description('The resource group the virtual network was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the virtual network.') +output resourceId string = virtualNetwork.id + +@description('The name of the virtual network.') +output name string = virtualNetwork.name + +@description('The names of the deployed subnets.') +output subnetNames array = [for subnet in subnets: subnet.name] + +@description('The resource IDs of the deployed subnets.') +output subnetResourceIds array = [for subnet in subnets: az.resourceId('Microsoft.Network/virtualNetworks/subnets', name, subnet.name)] + +@description('The location the resource was deployed into.') +output location string = virtualNetwork.location + +@description('The Diagnostic Settings of the virtual network.') +output diagnosticsLogs array = diagnosticsLogs diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/readme.md new file mode 100644 index 000000000..0e0f79fcf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/readme.md @@ -0,0 +1,740 @@ +# Virtual Networks `[Microsoft.Network/virtualNetworks]` + +This template deploys a virtual network (vNet). + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/virtualNetworks` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworks/virtualNetworkPeerings) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefixes` | array | An Array of 1 or more IP Address Prefixes for the Virtual Network. | +| `name` | string | The Virtual Network (vNet) Name. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `ddosProtectionPlanId` | string | `''` | | Resource ID of the DDoS protection plan to assign the VNET to. If it's left blank, DDoS protection will not be configured. If it's provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, VMProtectionAlerts]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of the diagnostic log analytics workspace. | +| `dnsServers` | array | `[]` | | DNS Servers associated to the Virtual Network. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `flowTimeoutInMinutes` | int | `0` | | The flow timeout in minutes for the Virtual Network, which is used to enable connection tracking for intra-VM flows. Possible values are between 4 and 30 minutes. Default value 0 will set the property to null. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `peerings` | array | `[]` | | Virtual Network Peerings configurations. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `subnets` | _[subnets](subnets/readme.md)_ array | `[]` | | An Array of subnets to deploy to the Virtual Network. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `vnetEncryption` | bool | `False` | | Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property. | +| `vnetEncryptionEnforcement` | string | `'AllowUnencrypted'` | `[AllowUnencrypted, DropUnencrypted]` | If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled. | + + +### Parameter Usage: `subnets` + +Below you can find an example for the subnet property's usage. For all remaining properties, please refer to the _[subnets](subnets/readme.md)_ readme. + +

+ +Template JSON format + +```json +"subnets": { + "value": [ + { + "name": "GatewaySubnet", + "addressPrefix": "10.0.255.0/24" + }, + { + "name": "<>-az-subnet-x-001", + "addressPrefix": "10.0.0.0/24", + "networkSecurityGroupId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001", + "serviceEndpoints": [ + { + "service": "Microsoft.Storage" + }, + { + "service": "Microsoft.Sql" + } + ], + "routeTableId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/routeTables/adp-<>-az-udr-x-001", + "delegations": [ + { + "name": "netappDel", + "properties": { + "serviceName": "Microsoft.Netapp/volumes" + } + } + ], + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +subnets: [ + { + name: 'GatewaySubnet' + addressPrefix: '10.0.255.0/24' + } + { + name: '<>-az-subnet-x-001' + addressPrefix: '10.0.0.0/24' + networkSecurityGroupId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/networkSecurityGroups/adp-<>-az-nsg-x-001' + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.Sql' + } + ] + routeTableId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/routeTables/adp-<>-az-udr-x-001' + delegations: [ + { + name: 'netappDel' + properties: { + serviceName: 'Microsoft.Netapp/volumes' + } + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } +] +``` + +
+

+ +### Parameter Usage: `virtualNetworkPeerings` + +As the virtual network peering array allows you to deploy not only a one-way but also two-way peering (i.e reverse), you can use the following ***additional*** properties on top of what is documented in _[virtualNetworkPeerings](virtualNetworkPeerings/readme.md)_. + +| Parameter Name | Type | Default Value | Possible Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `remotePeeringEnabled` | bool | `false` | | Optional. Set to true to also deploy the reverse peering for the configured remote virtual networks to the local network | +| `remotePeeringName` | string | `'${last(split(peering.remoteVirtualNetworkId, '/'))}-${name}'` | | Optional. The Name of Vnet Peering resource. If not provided, default value will be - | +| `remotePeeringAllowForwardedTraffic` | bool | `true` | | Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. | +| `remotePeeringAllowGatewayTransit` | bool | `false` | | Optional. If gateway links can be used in remote virtual networking to link to this virtual network. | +| `remotePeeringAllowVirtualNetworkAccess` | bool | `true` | | Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. | +| `remotePeeringDoNotVerifyRemoteGateways` | bool | `true` | | Optional. If we need to verify the provisioning state of the remote gateway. | +| `remotePeeringUseRemoteGateways` | bool | `false` | | Optional. If remote gateways can be used on this virtual network. If the flag is set to `true`, and allowGatewayTransit on local peering is also `true`, virtual network will use gateways of local virtual network for transit. Only one peering can have this flag set to `true`. This flag cannot be set if virtual network already has a gateway. | + +

+ +Parameter JSON format + +```json +"virtualNetworkPeerings": { + "value": [ + { + "remoteVirtualNetworkId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-peer01", + "allowForwardedTraffic": true, + "allowGatewayTransit": false, + "allowVirtualNetworkAccess": true, + "useRemoteGateways": false, + "remotePeeringEnabled": true, + "remotePeeringName": "customName", + "remotePeeringAllowVirtualNetworkAccess": true, + "remotePeeringAllowForwardedTraffic": true + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +virtualNetworkPeerings: [ + { + remoteVirtualNetworkId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-peer01' + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + useRemoteGateways: false + remotePeeringEnabled: true + remotePeeringName: 'customName' + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + } +] +``` + +
+

+ +### Parameter Usage: `addressPrefixes` + +The `addressPrefixes` parameter accepts a JSON Array of string values containing the IP Address Prefixes for the Virtual Network (vNet). + +Here's an example of specifying a single Address Prefix: + + +

+ +Parameter JSON format + +```json +"addressPrefixes": { + "value": [ + "10.1.0.0/16" + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +addressPrefixes: [ + '10.1.0.0/16' +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Considerations + +The network security group and route table resources must reside in the same resource group as the virtual network. + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `diagnosticsLogs` | array | The Diagnostic Settings of the virtual network. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual network. | +| `resourceGroupName` | string | The resource group the virtual network was deployed into. | +| `resourceId` | string | The resource ID of the virtual network. | +| `subnetNames` | array | The names of the deployed subnets. | +| `subnetResourceIds` | array | The resource IDs of the deployed subnets. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module virtualNetworks './Microsoft.Network/virtualNetworks/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvncom' + params: { + // Required parameters + addressPrefixes: [ + '10.0.0.0/16' + ] + name: '<>nvncom001' + // Non-required parameters + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + dnsServers: [ + '10.0.1.4' + '10.0.1.5' + ] + enableDefaultTelemetry: '' + flowTimeoutInMinutes: 20 + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + subnets: [ + { + addressPrefix: '10.0.255.0/24' + name: 'GatewaySubnet' + } + { + addressPrefix: '10.0.0.0/24' + name: '<>-az-subnet-x-001' + networkSecurityGroupId: '' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + routeTableId: '' + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.Sql' + } + ] + } + { + addressPrefix: '10.0.3.0/24' + delegations: [ + { + name: 'netappDel' + properties: { + serviceName: 'Microsoft.Netapp/volumes' + } + } + ] + name: '<>-az-subnet-x-002' + } + { + addressPrefix: '10.0.6.0/24' + name: '<>-az-subnet-x-003' + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "name": { + "value": "<>nvncom001" + }, + // Non-required parameters + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "dnsServers": { + "value": [ + "10.0.1.4", + "10.0.1.5" + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "flowTimeoutInMinutes": { + "value": 20 + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "subnets": { + "value": [ + { + "addressPrefix": "10.0.255.0/24", + "name": "GatewaySubnet" + }, + { + "addressPrefix": "10.0.0.0/24", + "name": "<>-az-subnet-x-001", + "networkSecurityGroupId": "", + "roleAssignments": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ], + "routeTableId": "", + "serviceEndpoints": [ + { + "service": "Microsoft.Storage" + }, + { + "service": "Microsoft.Sql" + } + ] + }, + { + "addressPrefix": "10.0.3.0/24", + "delegations": [ + { + "name": "netappDel", + "properties": { + "serviceName": "Microsoft.Netapp/volumes" + } + } + ], + "name": "<>-az-subnet-x-002" + }, + { + "addressPrefix": "10.0.6.0/24", + "name": "<>-az-subnet-x-003", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module virtualNetworks './Microsoft.Network/virtualNetworks/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvnmin' + params: { + // Required parameters + addressPrefixes: [ + '10.0.0.0/16' + ] + name: '<>nvnmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "name": { + "value": "<>nvnmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 3: Vnetpeering

+ +
+ +via Bicep module + +```bicep +module virtualNetworks './Microsoft.Network/virtualNetworks/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvnpeer' + params: { + // Required parameters + addressPrefixes: [ + '10.1.0.0/24' + ] + name: '<>nvnpeer001' + // Non-required parameters + enableDefaultTelemetry: '' + peerings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remotePeeringAllowForwardedTraffic: true + remotePeeringAllowVirtualNetworkAccess: true + remotePeeringEnabled: true + remotePeeringName: 'customName' + remoteVirtualNetworkId: '' + useRemoteGateways: false + } + ] + subnets: [ + { + addressPrefix: '10.1.0.0/26' + name: 'GatewaySubnet' + } + ] + tags: { + Environment: 'Non-Prod' + 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 + "addressPrefixes": { + "value": [ + "10.1.0.0/24" + ] + }, + "name": { + "value": "<>nvnpeer001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "peerings": { + "value": [ + { + "allowForwardedTraffic": true, + "allowGatewayTransit": false, + "allowVirtualNetworkAccess": true, + "remotePeeringAllowForwardedTraffic": true, + "remotePeeringAllowVirtualNetworkAccess": true, + "remotePeeringEnabled": true, + "remotePeeringName": "customName", + "remoteVirtualNetworkId": "", + "useRemoteGateways": false + } + ] + }, + "subnets": { + "value": [ + { + "addressPrefix": "10.1.0.0/26", + "name": "GatewaySubnet" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..6ba5f56ca --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2021-03-01' existing = { + name: '${split(resourceId, '/')[8]}/${split(resourceId, '/')[10]}' +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(subnet.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: subnet +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/deploy.bicep new file mode 100644 index 000000000..6844baac2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/deploy.bicep @@ -0,0 +1,126 @@ +@description('Optional. The Name of the subnet resource.') +param name string + +@description('Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment.') +param virtualNetworkName string + +@description('Required. The address prefix for the subnet.') +param addressPrefix string + +@description('Optional. The resource ID of the network security group to assign to the subnet.') +param networkSecurityGroupId string = '' + +@description('Optional. The resource ID of the route table to assign to the subnet.') +param routeTableId string = '' + +@description('Optional. The service endpoints to enable on the subnet.') +param serviceEndpoints array = [] + +@description('Optional. The delegations to enable on the subnet.') +param delegations array = [] + +@description('Optional. The resource ID of the NAT Gateway to use for the subnet.') +param natGatewayId string = '' + +@description('Optional. enable or disable apply network policies on private endpoint in the subnet.') +@allowed([ + 'Disabled' + 'Enabled' + '' +]) +param privateEndpointNetworkPolicies string = '' + +@description('Optional. enable or disable apply network policies on private link service in the subnet.') +@allowed([ + 'Disabled' + 'Enabled' + '' +]) +param privateLinkServiceNetworkPolicies string = '' + +@description('Optional. List of address prefixes for the subnet.') +param addressPrefixes array = [] + +@description('Optional. Application gateway IP configurations of virtual network resource.') +param applicationGatewayIpConfigurations array = [] + +@description('Optional. Array of IpAllocation which reference this subnet.') +param ipAllocations array = [] + +@description('Optional. An array of service endpoint policies.') +param serviceEndpointPolicies array = [] + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { + name: virtualNetworkName +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-07-01' = { + name: name + parent: virtualNetwork + properties: { + addressPrefix: addressPrefix + networkSecurityGroup: !empty(networkSecurityGroupId) ? { + id: networkSecurityGroupId + } : null + routeTable: !empty(routeTableId) ? { + id: routeTableId + } : null + natGateway: !empty(natGatewayId) ? { + id: natGatewayId + } : null + serviceEndpoints: serviceEndpoints + delegations: delegations + privateEndpointNetworkPolicies: !empty(privateEndpointNetworkPolicies) ? any(privateEndpointNetworkPolicies) : null + privateLinkServiceNetworkPolicies: !empty(privateLinkServiceNetworkPolicies) ? any(privateLinkServiceNetworkPolicies) : null + addressPrefixes: addressPrefixes + applicationGatewayIpConfigurations: applicationGatewayIpConfigurations + ipAllocations: ipAllocations + serviceEndpointPolicies: serviceEndpointPolicies + } +} + +module subnet_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, subnet.id)}-Subnet-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: subnet.id + } +}] + +@description('The resource group the virtual network peering was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual network peering.') +output name string = subnet.name + +@description('The resource ID of the virtual network peering.') +output resourceId string = subnet.id + +@description('The address prefix for the subnet.') +output subnetAddressPrefix string = subnet.properties.addressPrefix + +@description('List of address prefixes for the subnet.') +output subnetAddressPrefixes array = !empty(addressPrefixes) ? subnet.properties.addressPrefixes : [] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/readme.md new file mode 100644 index 000000000..80b2fc3b0 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/readme.md @@ -0,0 +1,200 @@ +# Virtual Network Subnets `[Microsoft.Network/virtualNetworks/subnets]` + +This module deploys a virtual network subnet. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## 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.Network/virtualNetworks/subnets` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworks/subnets) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefix` | string | The address prefix for the subnet. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `virtualNetworkName` | string | The name of the parent virtual network. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `addressPrefixes` | array | `[]` | | List of address prefixes for the subnet. | +| `applicationGatewayIpConfigurations` | array | `[]` | | Application gateway IP configurations of virtual network resource. | +| `delegations` | array | `[]` | | The delegations to enable on the subnet. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ipAllocations` | array | `[]` | | Array of IpAllocation which reference this subnet. | +| `name` | string | | | The Name of the subnet resource. | +| `natGatewayId` | string | `''` | | The resource ID of the NAT Gateway to use for the subnet. | +| `networkSecurityGroupId` | string | `''` | | The resource ID of the network security group to assign to the subnet. | +| `privateEndpointNetworkPolicies` | string | `''` | `['', Disabled, Enabled]` | enable or disable apply network policies on private endpoint in the subnet. | +| `privateLinkServiceNetworkPolicies` | string | `''` | `['', Disabled, Enabled]` | enable or disable apply network policies on private link service in the subnet. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `routeTableId` | string | `''` | | The resource ID of the route table to assign to the subnet. | +| `serviceEndpointPolicies` | array | `[]` | | An array of service endpoint policies. | +| `serviceEndpoints` | array | `[]` | | The service endpoints to enable on the subnet. | + + +### Parameter Usage: `delegations` + +

+ +Parameter JSON format + +```json +"delegations": [ + { + "name": "sqlMiDel", + "properties": { + "serviceName": "Microsoft.Sql/managedInstances" + } + } +] +``` + +
+ +
+ +Bicep format + +```bicep +delegations: [ + { + name: 'sqlMiDel' + properties: { + serviceName: 'Microsoft.Sql/managedInstances' + } + } +] +``` + +
+

+ +### Parameter Usage: `serviceEndpoints` + +

+ +Parameter JSON format + +```json +"serviceEndpoints": [ + "Microsoft.EventHub", + "Microsoft.Sql", + "Microsoft.Storage", + "Microsoft.KeyVault" +] +``` + +
+ + +
+ +Bicep format + +```bicep +serviceEndpoints: [ + 'Microsoft.EventHub' + 'Microsoft.Sql' + 'Microsoft.Storage' + 'Microsoft.KeyVault' +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Considerations + +The `privateEndpointNetworkPolicies` property must be set to disabled for subnets that contain private endpoints. It confirms that NSGs rules will not apply to private endpoints (currently not supported, [reference](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#limitations)). Default Value when not specified is "Enabled". + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the virtual network peering. | +| `resourceGroupName` | string | The resource group the virtual network peering was deployed into. | +| `resourceId` | string | The resource ID of the virtual network peering. | +| `subnetAddressPrefix` | string | The address prefix for the subnet. | +| `subnetAddressPrefixes` | array | List of address prefixes for the subnet. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/subnets/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep new file mode 100644 index 000000000..c480aaf83 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/deploy.bicep @@ -0,0 +1,66 @@ +@description('Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName.') +param name string = '${localVnetName}-${last(split(remoteVirtualNetworkId, '/'))}' + +@description('Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment.') +param localVnetName string + +@description('Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID.') +param remoteVirtualNetworkId string + +@description('Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true.') +param allowForwardedTraffic bool = true + +@description('Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false.') +param allowGatewayTransit bool = false + +@description('Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true.') +param allowVirtualNetworkAccess bool = true + +@description('Optional. If we need to verify the provisioning state of the remote gateway. Default is true.') +param doNotVerifyRemoteGateways bool = true + +@description('Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false.') +param useRemoteGateways bool = false + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { + name: localVnetName +} + +resource virtualNetworkPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2022-07-01' = { + name: name + parent: virtualNetwork + properties: { + allowForwardedTraffic: allowForwardedTraffic + allowGatewayTransit: allowGatewayTransit + allowVirtualNetworkAccess: allowVirtualNetworkAccess + doNotVerifyRemoteGateways: doNotVerifyRemoteGateways + useRemoteGateways: useRemoteGateways + remoteVirtualNetwork: { + id: remoteVirtualNetworkId + } + } +} + +@description('The resource group the virtual network peering was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the virtual network peering.') +output name string = virtualNetworkPeering.name + +@description('The resource ID of the virtual network peering.') +output resourceId string = virtualNetworkPeering.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md new file mode 100644 index 000000000..9d62829d8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/readme.md @@ -0,0 +1,62 @@ +# VirtualNetworkPeering `[Microsoft.Network/virtualNetworks/virtualNetworkPeerings]` + +This template deploys Virtual Network Peering. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualNetworks/virtualNetworkPeerings) | + +### Resource dependency + +The following resources are required to be able to deploy this resource. + +- Local Virtual Network (Identified by the `localVnetName` parameter). +- Remote Virtual Network (Identified by the `remoteVirtualNetworkId` parameter) + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `remoteVirtualNetworkId` | string | The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `localVnetName` | string | The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `allowForwardedTraffic` | bool | `True` | Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true. | +| `allowGatewayTransit` | bool | `False` | If gateway links can be used in remote virtual networking to link to this virtual network. Default is false. | +| `allowVirtualNetworkAccess` | bool | `True` | Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true. | +| `doNotVerifyRemoteGateways` | bool | `True` | If we need to verify the provisioning state of the remote gateway. Default is true. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `name` | string | `[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]` | The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName. | +| `useRemoteGateways` | bool | `False` | If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the virtual network peering. | +| `resourceGroupName` | string | The resource group the virtual network peering was deployed into. | +| `resourceId` | string | The resource ID of the virtual network peering. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualNetworks/virtualNetworkPeerings/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..b93a816bb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,97 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource virtualWan 'Microsoft.Network/virtualWans@2021-08-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(virtualWan.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: virtualWan +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/dependencies.bicep new file mode 100644 index 000000000..7371d4437 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/dependencies.bicep @@ -0,0 +1,14 @@ +@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 + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/deploy.test.bicep new file mode 100644 index 000000000..1c0a32f8f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/common/deploy.test.bicep @@ -0,0 +1,68 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualwans-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvwcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + allowBranchToBranchTraffic: true + allowVnetToVnetTraffic: true + disableVpnEncryption: true + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + type: 'Basic' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/min/deploy.test.bicep new file mode 100644 index 000000000..0bb5e9721 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.virtualwans-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvwmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/deploy.bicep new file mode 100644 index 000000000..ca0123b70 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/deploy.bicep @@ -0,0 +1,96 @@ +@description('Optional. Location where all resources will be created.') +param location string = resourceGroup().location + +@description('Required. Name of the Virtual WAN.') +param name string + +@description('Optional. The type of the Virtual WAN.') +@allowed([ + 'Standard' + 'Basic' +]) +param type string = 'Standard' + +@description('Optional. True if branch to branch traffic is allowed.') +param allowBranchToBranchTraffic bool = false + +@description('Optional. True if VNET to VNET traffic is allowed.') +param allowVnetToVnetTraffic bool = false + +@description('Optional. VPN encryption to be disabled or not.') +param disableVpnEncryption bool = false + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource virtualWan 'Microsoft.Network/virtualWans@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + allowBranchToBranchTraffic: allowBranchToBranchTraffic + allowVnetToVnetTraffic: allowVnetToVnetTraffic ? allowVnetToVnetTraffic : null + disableVpnEncryption: disableVpnEncryption + type: type + } +} + +resource virtualWan_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${virtualWan.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: virtualWan +} + +module virtualWan_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VWan-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: virtualWan.id + } +}] + +@description('The name of the virtual WAN.') +output name string = virtualWan.name + +@description('The resource ID of the virtual WAN.') +output resourceId string = virtualWan.id + +@description('The resource group the virtual WAN was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = virtualWan.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/readme.md new file mode 100644 index 000000000..808ff3e6b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/readme.md @@ -0,0 +1,302 @@ +# Virtual WANs `[Microsoft.Network/virtualWans]` + +This template deploys a virtual WAN. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/virtualWans` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/virtualWans) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Virtual WAN. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `allowBranchToBranchTraffic` | bool | `False` | | True if branch to branch traffic is allowed. | +| `allowVnetToVnetTraffic` | bool | `False` | | True if VNET to VNET traffic is allowed. | +| `disableVpnEncryption` | bool | `False` | | VPN encryption to be disabled or not. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | | Location where all resources will be created. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `type` | string | `'Standard'` | `[Basic, Standard]` | The type of the Virtual WAN. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the virtual WAN. | +| `resourceGroupName` | string | The resource group the virtual WAN was deployed into. | +| `resourceId` | string | The resource ID of the virtual WAN. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module virtualWans './Microsoft.Network/virtualWans/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvwcom' + params: { + // Required parameters + name: '<>nvwcom001' + // Non-required parameters + allowBranchToBranchTraffic: true + allowVnetToVnetTraffic: true + disableVpnEncryption: true + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + type: 'Basic' + } +} +``` + +
+

+ +

+ +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": "<>nvwcom001" + }, + // Non-required parameters + "allowBranchToBranchTraffic": { + "value": true + }, + "allowVnetToVnetTraffic": { + "value": true + }, + "disableVpnEncryption": { + "value": true + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "type": { + "value": "Basic" + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module virtualWans './Microsoft.Network/virtualWans/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvwmin' + params: { + // Required parameters + name: '<>nvwmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nvwmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/virtualWans/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/dependencies.bicep new file mode 100644 index 000000000..cc25cd12d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/dependencies.bicep @@ -0,0 +1,49 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Optional. The name of the Virtual Hub to create.') +param virtualHubName string + +@description('Optional. The name of the VPN Site to create.') +param vpnSiteName string + +@description('Required. The name of the virtual WAN to create.') +param virtualWANName string + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-01-01' = { + name: virtualHubName + location: location + properties: { + virtualWan: { + id: virtualWan.id + } + addressPrefix: '10.0.0.0/24' + } +} + +resource vpnSite 'Microsoft.Network/vpnSites@2022-01-01' = { + name: vpnSiteName + location: location + properties: { + virtualWan: { + id: virtualWan.id + } + addressSpace: { + addressPrefixes: [ + '10.1.0.0/16' + ] + } + ipAddress: '10.1.0.0' + } +} + +@description('The resource ID of the created Virtual Hub.') +output virtualHubResourceId string = virtualHub.id + +@description('The resource ID of the created VPN site.') +output vpnSiteResourceId string = vpnSite.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/deploy.test.bicep new file mode 100644 index 000000000..3b458c0bc --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/common/deploy.test.bicep @@ -0,0 +1,92 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.vpngateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualHubName: 'dep-<>-vh-${serviceShort}' + virtualWANName: 'dep-<>-vw-${serviceShort}' + vpnSiteName: 'dep-<>-vs-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId + bgpSettings: { + asn: 65515 + peerWeight: 0 + } + connections: [ + { + connectionBandwidth: 100 + enableBgp: false + name: 'Connection-${last(split(nestedDependencies.outputs.vpnSiteResourceId, '/'))}' + remoteVpnSiteResourceId: nestedDependencies.outputs.vpnSiteResourceId + enableInternetSecurity: true + vpnConnectionProtocolType: 'IKEv2' + enableRateLimiting: false + useLocalAzureIpAddress: false + usePolicyBasedTrafficSelectors: false + routingWeight: 0 + } + ] + lock: 'CanNotDelete' + natRules: [ + { + externalMappings: [ + { + addressSpace: '192.168.21.0/24' + } + ] + internalMappings: [ + { + addressSpace: '10.4.0.0/24' + } + ] + mode: 'EgressSnat' + name: 'natRule1' + type: 'Static' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/dependencies.bicep new file mode 100644 index 000000000..e8e34ac82 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/dependencies.bicep @@ -0,0 +1,27 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Optional. The name of the Virtual Hub to create.') +param virtualHubName string + +@description('Required. The name of the virtual WAN to create.') +param virtualWANName string + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +resource virtualHub 'Microsoft.Network/virtualHubs@2022-01-01' = { + name: virtualHubName + location: location + properties: { + virtualWan: { + id: virtualWan.id + } + addressPrefix: '10.1.0.0/16' + } +} + +@description('The resource ID of the created Virtual Hub.') +output virtualHubResourceId string = virtualHub.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/deploy.test.bicep new file mode 100644 index 000000000..a45bc451c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/.test/min/deploy.test.bicep @@ -0,0 +1,52 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.vpngateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualHubName: 'dep-<>-vh-${serviceShort}' + virtualWANName: 'dep-<>-vw-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + virtualHubResourceId: nestedDependencies.outputs.virtualHubResourceId + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/deploy.bicep new file mode 100644 index 000000000..96d791f54 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/deploy.bicep @@ -0,0 +1,102 @@ +@description('Required. The name of the VPN connection.') +param name string + +@description('Conditional. The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment.') +param vpnGatewayName string + +@description('Optional. The IPSec policies to be considered by this connection.') +param ipsecPolicies array = [] + +@description('Optional. The traffic selector policies to be considered by this connection.') +param trafficSelectorPolicies array = [] + +@description('Optional. List of all VPN site link connections to the gateway.') +param vpnLinkConnections array = [] + +@description('Optional. Routing configuration indicating the associated and propagated route tables for this connection.') +param routingConfiguration object = {} + +@description('Optional. Enable policy-based traffic selectors.') +param usePolicyBasedTrafficSelectors bool = false + +@description('Optional. Use local Azure IP to initiate connection.') +param useLocalAzureIpAddress bool = false + +@description('Optional. Enable rate limiting.') +param enableRateLimiting bool = false + +@description('Optional. Enable internet security.') +param enableInternetSecurity bool = false + +@description('Optional. Enable BGP flag.') +param enableBgp bool = false + +@description('Optional. Routing weight for VPN connection.') +param routingWeight int = 0 + +@description('Optional. Expected bandwidth in MBPS.') +param connectionBandwidth int = 10 + +@description('Optional. Gateway connection protocol.') +@allowed([ + 'IKEv1' + 'IKEv2' +]) +param vpnConnectionProtocolType string = 'IKEv2' + +@description('Optional. SharedKey for the VPN connection.') +param sharedKey string = '' + +@description('Optional. Reference to a VPN site to link to.') +param remoteVpnSiteResourceId string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnGateway 'Microsoft.Network/vpnGateways@2022-07-01' existing = { + name: vpnGatewayName +} + +resource vpnConnection 'Microsoft.Network/vpnGateways/vpnConnections@2022-07-01' = { + name: name + parent: vpnGateway + properties: { + connectionBandwidth: connectionBandwidth + enableBgp: enableBgp + enableInternetSecurity: enableInternetSecurity + enableRateLimiting: enableRateLimiting + ipsecPolicies: ipsecPolicies + remoteVpnSite: !empty(remoteVpnSiteResourceId) ? { + id: remoteVpnSiteResourceId + } : null + routingConfiguration: routingConfiguration + routingWeight: routingWeight + sharedKey: sharedKey + trafficSelectorPolicies: trafficSelectorPolicies + useLocalAzureIpAddress: useLocalAzureIpAddress + usePolicyBasedTrafficSelectors: usePolicyBasedTrafficSelectors + vpnConnectionProtocolType: vpnConnectionProtocolType + vpnLinkConnections: vpnLinkConnections + } +} + +@description('The name of the VPN connection.') +output name string = vpnConnection.name + +@description('The resource ID of the VPN connection.') +output resourceId string = vpnConnection.id + +@description('The name of the resource group the VPN connection was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/readme.md new file mode 100644 index 000000000..735f00667 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/readme.md @@ -0,0 +1,120 @@ +# VPN Gateways Connections `[Microsoft.Network/vpnGateways/connections]` + +This module deploys VPN Gateways Connections. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/vpnGateways/vpnConnections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/vpnGateways/vpnConnections) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the VPN connection. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `vpnGatewayName` | string | The name of the parent VPN gateway this VPN connection is associated with. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `connectionBandwidth` | int | `10` | | Expected bandwidth in MBPS. | +| `enableBgp` | bool | `False` | | Enable BGP flag. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `enableInternetSecurity` | bool | `False` | | Enable internet security. | +| `enableRateLimiting` | bool | `False` | | Enable rate limiting. | +| `ipsecPolicies` | array | `[]` | | The IPSec policies to be considered by this connection. | +| `remoteVpnSiteResourceId` | string | `''` | | Reference to a VPN site to link to. | +| `routingConfiguration` | object | `{object}` | | Routing configuration indicating the associated and propagated route tables for this connection. | +| `routingWeight` | int | `0` | | Routing weight for VPN connection. | +| `sharedKey` | string | `''` | | SharedKey for the VPN connection. | +| `trafficSelectorPolicies` | array | `[]` | | The traffic selector policies to be considered by this connection. | +| `useLocalAzureIpAddress` | bool | `False` | | Use local Azure IP to initiate connection. | +| `usePolicyBasedTrafficSelectors` | bool | `False` | | Enable policy-based traffic selectors. | +| `vpnConnectionProtocolType` | string | `'IKEv2'` | `[IKEv1, IKEv2]` | Gateway connection protocol. | +| `vpnLinkConnections` | array | `[]` | | List of all VPN site link connections to the gateway. | + + +### Parameter Usage: `routingConfiguration` + +

+ +Parameter JSON format + +```json +"routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable" + }, + "propagatedRouteTables": { + "labels": [ + "default" + ], + "ids": [ + { + "id": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable" + } + ] + }, + "vnetRoutes": { + "staticRoutes": [] + } +} +``` + +
+ +
+ +Bicep format + +```bicep +routingConfiguration: { + associatedRouteTable: { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable' + } + propagatedRouteTables: { + labels: [ + 'default' + ] + ids: [ + { + id: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualHubs/SampleVirtualHub/hubRouteTables/defaultRouteTable' + } + ] + } + vnetRoutes: { + staticRoutes: [] + } +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the VPN connection. | +| `resourceGroupName` | string | The name of the resource group the VPN connection was deployed into. | +| `resourceId` | string | The resource ID of the VPN connection. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/connections/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/deploy.bicep new file mode 100644 index 000000000..11229152d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/deploy.bicep @@ -0,0 +1,124 @@ +@description('Required. Name of the VPN gateway.') +param name string + +@description('Optional. Location where all resources will be created.') +param location string = resourceGroup().location + +@description('Optional. The connections to create in the VPN gateway.') +param connections array = [] + +@description('Optional. List of all the NAT Rules to associate with the gateway.') +param natRules array = [] + +@description('Required. The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location.') +param virtualHubResourceId string + +@description('Optional. BGP settings details.') +param bgpSettings object = {} + +@description('Optional. Enable BGP routes translation for NAT on this VPN gateway.') +param enableBgpRouteTranslationForNat bool = false + +@description('Optional. Enable routing preference property for the public IP interface of the VPN gateway.') +param isRoutingPreferenceInternet bool = false + +@description('Optional. The scale unit for this VPN gateway.') +param vpnGatewayScaleUnit int = 2 + +@description('Optional. Tags of the resource.') +param tags object = {} + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnGateway 'Microsoft.Network/vpnGateways@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + bgpSettings: bgpSettings + enableBgpRouteTranslationForNat: enableBgpRouteTranslationForNat + isRoutingPreferenceInternet: isRoutingPreferenceInternet + vpnGatewayScaleUnit: vpnGatewayScaleUnit + virtualHub: { + id: virtualHubResourceId + } + } +} + +resource vpnGateway_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${vpnGateway.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: vpnGateway +} + +module vpnGateway_natRules 'natRules/deploy.bicep' = [for (natRule, index) in natRules: { + name: '${deployment().name}-NATRule-${index}' + params: { + name: natRule.name + vpnGatewayName: vpnGateway.name + externalMappings: contains(natRule, 'externalMappings') ? natRule.externalMappings : [] + internalMappings: contains(natRule, 'internalMappings') ? natRule.internalMappings : [] + ipConfigurationId: contains(natRule, 'ipConfigurationId') ? natRule.ipConfigurationId : '' + mode: contains(natRule, 'mode') ? natRule.mode : '' + type: contains(natRule, 'type') ? natRule.type : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module vpnGateway_connections 'connections/deploy.bicep' = [for (connection, index) in connections: { + name: '${deployment().name}-Connection-${index}' + params: { + name: connection.name + vpnGatewayName: vpnGateway.name + connectionBandwidth: contains(connection, 'connectionBandwidth') ? connection.connectionBandwidth : 10 + enableBgp: contains(connection, 'enableBgp') ? connection.enableBgp : false + enableInternetSecurity: contains(connection, 'enableInternetSecurity') ? connection.enableInternetSecurity : false + remoteVpnSiteResourceId: contains(connection, 'remoteVpnSiteResourceId') ? connection.remoteVpnSiteResourceId : '' + enableRateLimiting: contains(connection, 'enableRateLimiting') ? connection.enableRateLimiting : false + routingConfiguration: contains(connection, 'routingConfiguration') ? connection.routingConfiguration : {} + routingWeight: contains(connection, 'routingWeight') ? connection.routingWeight : 0 + sharedKey: contains(connection, 'sharedKey') ? connection.sharedKey : '' + useLocalAzureIpAddress: contains(connection, 'useLocalAzureIpAddress') ? connection.useLocalAzureIpAddress : false + usePolicyBasedTrafficSelectors: contains(connection, 'usePolicyBasedTrafficSelectors') ? connection.usePolicyBasedTrafficSelectors : false + vpnConnectionProtocolType: contains(connection, 'vpnConnectionProtocolType') ? connection.vpnConnectionProtocolType : 'IKEv2' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +@description('The name of the VPN gateway.') +output name string = vpnGateway.name + +@description('The resource ID of the VPN gateway.') +output resourceId string = vpnGateway.id + +@description('The name of the resource group the VPN gateway was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = vpnGateway.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/deploy.bicep new file mode 100644 index 000000000..454eb5491 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/deploy.bicep @@ -0,0 +1,70 @@ +@description('Required. The name of the NAT rule.') +param name string + +@description('Conditional. The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment.') +param vpnGatewayName string + +@description('Optional. An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range.') +param externalMappings array = [] + +@description('Optional. An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range.') +param internalMappings array = [] + +@description('Optional. A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances.') +param ipConfigurationId string = '' + +@description('Optional. The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub\'s site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub\'s Site-to-site VPN gateway.') +@allowed([ + '' + 'EgressSnat' + 'IngressSnat' +]) +param mode string = '' + +@description('Optional. The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability.') +@allowed([ + '' + 'Dynamic' + 'Static' +]) +param type string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnGateway 'Microsoft.Network/vpnGateways@2022-07-01' existing = { + name: vpnGatewayName +} + +resource natRule 'Microsoft.Network/vpnGateways/natRules@2022-07-01' = { + name: name + parent: vpnGateway + properties: { + externalMappings: externalMappings + internalMappings: internalMappings + ipConfigurationId: !empty(ipConfigurationId) ? ipConfigurationId : null + mode: !empty(mode) ? any(mode) : null + type: !empty(type) ? any(type) : null + } +} + +@description('The name of the NAT rule.') +output name string = natRule.name + +@description('The resource ID of the NAT rule.') +output resourceId string = natRule.id + +@description('The name of the resource group the NAT rule was deployed into.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/readme.md new file mode 100644 index 000000000..2a8336be4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/readme.md @@ -0,0 +1,54 @@ +# VPN Gateways NATRules `[Microsoft.Network/vpnGateways/natRules]` + +This module deploys VPN Gateways NATRules + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Network/vpnGateways/natRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/vpnGateways/natRules) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NAT rule. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `vpnGatewayName` | string | The name of the parent VPN gateway this NAT rule is associated with. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `externalMappings` | array | `[]` | | An address prefix range of destination IPs on the outside network that source IPs will be mapped to. In other words, your post-NAT address prefix range. | +| `internalMappings` | array | `[]` | | An address prefix range of source IPs on the inside network that will be mapped to a set of external IPs. In other words, your pre-NAT address prefix range. | +| `ipConfigurationId` | string | `''` | | A NAT rule must be configured to a specific VPN Gateway instance. This is applicable to Dynamic NAT only. Static NAT rules are automatically applied to both VPN Gateway instances. | +| `mode` | string | `''` | `['', EgressSnat, IngressSnat]` | The type of NAT rule for VPN NAT. IngressSnat mode (also known as Ingress Source NAT) is applicable to traffic entering the Azure hub's site-to-site VPN gateway. EgressSnat mode (also known as Egress Source NAT) is applicable to traffic leaving the Azure hub's Site-to-site VPN gateway. | +| `type` | string | `''` | `['', Dynamic, Static]` | The type of NAT rule for VPN NAT. Static one-to-one NAT establishes a one-to-one relationship between an internal address and an external address while Dynamic NAT assigns an IP and port based on availability. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the NAT rule. | +| `resourceGroupName` | string | The name of the resource group the NAT rule was deployed into. | +| `resourceId` | string | The resource ID of the NAT rule. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/natRules/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/readme.md new file mode 100644 index 000000000..6231b2d0e --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/readme.md @@ -0,0 +1,371 @@ +# VPN Gateways `[Microsoft.Network/vpnGateways]` + +This module deploys VPN Gateways. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Network/vpnGateways` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/vpnGateways) | +| `Microsoft.Network/vpnGateways/natRules` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/vpnGateways/natRules) | +| `Microsoft.Network/vpnGateways/vpnConnections` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/vpnGateways/vpnConnections) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the VPN gateway. | +| `virtualHubResourceId` | string | The resource ID of a virtual Hub to connect to. Note: The virtual Hub and Gateway must be deployed into the same location. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `bgpSettings` | object | `{object}` | | BGP settings details. | +| `connections` | _[connections](connections/readme.md)_ array | `[]` | | The connections to create in the VPN gateway. | +| `enableBgpRouteTranslationForNat` | bool | `False` | | Enable BGP routes translation for NAT on this VPN gateway. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `isRoutingPreferenceInternet` | bool | `False` | | Enable routing preference property for the public IP interface of the VPN gateway. | +| `location` | string | `[resourceGroup().location]` | | Location where all resources will be created. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `natRules` | _[natRules](natRules/readme.md)_ array | `[]` | | List of all the NAT Rules to associate with the gateway. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `vpnGatewayScaleUnit` | int | `2` | | The scale unit for this VPN gateway. | + + +### Parameter Usage: `bgpSettings` + +

+ +Parameter JSON format + +```json +"bgpSettings": { + "asn": 65515, + "peerWeight": 0, + "bgpPeeringAddresses": [ + { + "ipconfigurationId": "Instance0", + "defaultBgpIpAddresses": [ + "10.0.0.12" + ], + "customBgpIpAddresses": [], + "tunnelIpAddresses": [ + "20.84.35.53", + "10.0.0.4" + ] + }, + { + "ipconfigurationId": "Instance1", + "defaultBgpIpAddresses": [ + "10.0.0.13" + ], + "customBgpIpAddresses": [], + "tunnelIpAddresses": [ + "20.84.34.225", + "10.0.0.5" + ] + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +bgpSettings: { + asn: 65515 + peerWeight: 0 + bgpPeeringAddresses: [ + { + ipconfigurationId: 'Instance0' + defaultBgpIpAddresses: [ + '10.0.0.12' + ] + customBgpIpAddresses: [] + tunnelIpAddresses: [ + '20.84.35.53' + '10.0.0.4' + ] + } + { + ipconfigurationId: 'Instance1' + defaultBgpIpAddresses: [ + '10.0.0.13' + ] + customBgpIpAddresses: [] + tunnelIpAddresses: [ + '20.84.34.225' + '10.0.0.5' + ] + } + ] +} +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the VPN gateway. | +| `resourceGroupName` | string | The name of the resource group the VPN gateway was deployed into. | +| `resourceId` | string | The resource ID of the VPN gateway. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module vpnGateways './Microsoft.Network/vpnGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvgcom' + params: { + // Required parameters + name: '<>nvgcom001' + virtualHubResourceId: '' + // Non-required parameters + bgpSettings: { + asn: 65515 + peerWeight: 0 + } + connections: [ + { + connectionBandwidth: 100 + enableBgp: false + enableInternetSecurity: true + enableRateLimiting: false + name: '' + remoteVpnSiteResourceId: '' + routingWeight: 0 + useLocalAzureIpAddress: false + usePolicyBasedTrafficSelectors: false + vpnConnectionProtocolType: 'IKEv2' + } + ] + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + natRules: [ + { + externalMappings: [ + { + addressSpace: '192.168.21.0/24' + } + ] + internalMappings: [ + { + addressSpace: '10.4.0.0/24' + } + ] + mode: 'EgressSnat' + name: 'natRule1' + type: 'Static' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>nvgcom001" + }, + "virtualHubResourceId": { + "value": "" + }, + // Non-required parameters + "bgpSettings": { + "value": { + "asn": 65515, + "peerWeight": 0 + } + }, + "connections": { + "value": [ + { + "connectionBandwidth": 100, + "enableBgp": false, + "enableInternetSecurity": true, + "enableRateLimiting": false, + "name": "", + "remoteVpnSiteResourceId": "", + "routingWeight": 0, + "useLocalAzureIpAddress": false, + "usePolicyBasedTrafficSelectors": false, + "vpnConnectionProtocolType": "IKEv2" + } + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "natRules": { + "value": [ + { + "externalMappings": [ + { + "addressSpace": "192.168.21.0/24" + } + ], + "internalMappings": [ + { + "addressSpace": "10.4.0.0/24" + } + ], + "mode": "EgressSnat", + "name": "natRule1", + "type": "Static" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module vpnGateways './Microsoft.Network/vpnGateways/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvgmin' + params: { + // Required parameters + name: '<>nvgmin001' + virtualHubResourceId: '' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>nvgmin001" + }, + "virtualHubResourceId": { + "value": "" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnGateways/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..8095acfbf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,63 @@ +param principalIds array +param principalType string = '' +param roleDefinitionIdOrName string +param resourceId string + +var builtInRoleNames = { + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + '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') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Virtual Machine User Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52') + 'Windows Admin Center Administrator Login': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a6333a3e-0164-44c3-b281-7a577aff287f') +} + +resource vpnSite 'Microsoft.Network/vpnSites@2022-07-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(vpnSite.id, principalId, roleDefinitionIdOrName) + properties: { + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + } + scope: vpnSite +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/dependencies.bicep new file mode 100644 index 000000000..958f2e365 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/dependencies.bicep @@ -0,0 +1,24 @@ +@description('Required. The name of the managed identity to create.') +param managedIdentityName string + +@description('Required. The name of the virtual WAN to create.') +param virtualWANName 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 +} + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +@description('The principal ID of the created managed identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Virtual WAN.') +output virtualWWANResourceId string = virtualWan.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/deploy.test.bicep new file mode 100644 index 000000000..508210b48 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/common/deploy.test.bicep @@ -0,0 +1,106 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.vpnSites-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvscom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + virtualWANName: 'dep-<>-vw-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>-${serviceShort}' + virtualWanId: nestedDependencies.outputs.virtualWWANResourceId + lock: 'CanNotDelete' + tags: { + tagA: 'valueA' + tagB: 'valueB' + } + deviceProperties: { + linkSpeedInMbps: 0 + } + vpnSiteLinks: [ + { + name: '<>-vSite-${serviceShort}' + properties: { + bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + } + ipAddress: '1.2.3.4' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } + { + name: 'Link1' + properties: { + bgpProperties: { + asn: 65020 + bgpPeeringAddress: '192.168.1.0' + } + ipAddress: '2.2.2.2' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } + ] + o365Policy: { + breakOutCategories: { + optimize: true + allow: true + default: true + } + } + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/dependencies.bicep new file mode 100644 index 000000000..6b1819ebe --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Required. The name of the virtual WAN to create.') +param virtualWANName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource virtualWan 'Microsoft.Network/virtualWans@2021-05-01' = { + name: virtualWANName + location: location +} + +@description('The resource ID of the created Virtual WAN.') +output virtualWWANResourceId string = virtualWan.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/deploy.test.bicep new file mode 100644 index 000000000..2a3f92614 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/.test/min/deploy.test.bicep @@ -0,0 +1,56 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.network.vpnSites-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'nvsmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + virtualWANName: 'dep-<>-vw-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>-${serviceShort}' + virtualWanId: nestedDependencies.outputs.virtualWWANResourceId + addressPrefixes: [ + '10.0.0.0/16' + ] + ipAddress: '1.2.3.4' + } +} + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/deploy.bicep new file mode 100644 index 000000000..611dd22d7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/deploy.bicep @@ -0,0 +1,108 @@ +@description('Required. Name of the VPN Site.') +param name string + +@description('Required. Resource ID of the virtual WAN to link to.') +param virtualWanId string + +@description('Optional. Location where all resources will be created.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Conditional. An array of IP address ranges that can be used by subnets of the virtual network. Required if no bgpProperties or VPNSiteLinks are configured.') +param addressPrefixes array = [] + +@description('Conditional. BGP settings details. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. Required if no addressPrefixes or VPNSiteLinks are configured.') +param bgpProperties object = {} + +@description('Optional. List of properties of the device.') +param deviceProperties object = {} + +@description('Optional. The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead.') +param ipAddress string = '' + +@description('Optional. IsSecuritySite flag.') +param isSecuritySite bool = false + +@description('Optional. The Office365 breakout policy.') +param o365Policy object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. List of all VPN site links.') +param vpnSiteLinks array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource vpnSite 'Microsoft.Network/vpnSites@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + addressSpace: !empty(addressPrefixes) ? { + addressPrefixes: addressPrefixes + } : null + bgpProperties: !empty(bgpProperties) ? bgpProperties : null + deviceProperties: !empty(deviceProperties) ? deviceProperties : null + ipAddress: !empty(ipAddress) ? ipAddress : null + isSecuritySite: isSecuritySite + o365Policy: !empty(o365Policy) ? o365Policy : null + virtualWan: { + id: virtualWanId + } + vpnSiteLinks: !empty(vpnSiteLinks) ? vpnSiteLinks : null + } +} + +resource vpnSite_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${vpnSite.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: vpnSite +} + +module vpnSite_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-VWan-Rbac-${index}' + params: { + principalIds: roleAssignment.principalIds + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: vpnSite.id + } +}] + +@description('The name of the VPN site.') +output name string = vpnSite.name + +@description('The resource ID of the VPN site.') +output resourceId string = vpnSite.id + +@description('The resource group the VPN site was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = vpnSite.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/readme.md new file mode 100644 index 000000000..2133c3f19 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/readme.md @@ -0,0 +1,566 @@ +# VPN Sites `[Microsoft.Network/vpnSites]` + +This module deploys a VPN Site. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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/vpnSites` | [2022-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-07-01/vpnSites) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the VPN Site. | +| `virtualWanId` | string | Resource ID of the virtual WAN to link to. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `addressPrefixes` | array | An array of IP address ranges that can be used by subnets of the virtual network. Required if no bgpProperties or VPNSiteLinks are configured. | +| `bgpProperties` | object | BGP settings details. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. Required if no addressPrefixes or VPNSiteLinks are configured. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `deviceProperties` | object | `{object}` | | List of properties of the device. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `ipAddress` | string | `''` | | The IP-address for the VPN-site. Note: This is a deprecated property, please use the corresponding VpnSiteLinks property instead. | +| `isSecuritySite` | bool | `False` | | IsSecuritySite flag. | +| `location` | string | `[resourceGroup().location]` | | Location where all resources will be created. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `o365Policy` | object | `{object}` | | The Office365 breakout policy. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `vpnSiteLinks` | array | `[]` | | List of all VPN site links. | + + +### Parameter Usage `o365Policy` + +

+ +Parameter JSON format + +```json +"o365Policy": { + "value": { + "breakOutCategories": { + "optimize": true, + "allow": true, + "default": true + } + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +o365Policy: { + breakOutCategories: { + optimize: true + allow: true + default: true + } +} +``` + +
+

+ +### Parameter Usage `deviceProperties` + +

+ +Parameter JSON format + +```json +"deviceProperties": { + "value": { + "deviceModel": "morty", + "deviceVendor": "contoso", + "linkSpeedInMbps": 0 + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +deviceProperties: { + deviceModel: 'morty' + deviceVendor: 'contoso' + linkSpeedInMbps: 0 +} +``` + +
+

+ +### Parameter Usage `bgpProperties` + +The BGP properties. Note: This is a deprecated property, please use the corresponding `VpnSiteLinks` property instead. + +

+ +Parameter JSON format + +```json +"bgpProperties": { + "value": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1", + "peerWeight": 0 + } +} +``` + +
+ + +
+ +Bicep format + +```bicep +bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + peerWeight: 0 +} +``` + +
+

+ +### Parameter Usage `vpnSiteLinks` + +An array of links. Should be used instead of the top-level `ipAddress` & `bgpProperties` properties. If using links, one default link with same name and properties as VpnSite itself is mandatory. + +

+ +Parameter JSON format + +```json +"vpnSiteLinks": { + "value": [ + { + "name": "<>-az-vSite-x-001", + "properties": { + "bgpProperties": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1" + }, + "ipAddress": "1.2.3.4", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +vpnSiteLinks: [ + { + name: '<>-az-vSite-x-001' + properties: { + bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + } + ipAddress: '1.2.3.4' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the VPN site. | +| `resourceGroupName` | string | The resource group the VPN site was deployed into. | +| `resourceId` | string | The resource ID of the VPN site. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module vpnSites './Microsoft.Network/vpnSites/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvscom' + params: { + // Required parameters + name: '<>-nvscom' + virtualWanId: '' + // Non-required parameters + deviceProperties: { + linkSpeedInMbps: 0 + } + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + o365Policy: { + breakOutCategories: { + allow: true + default: true + optimize: true + } + } + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + tagA: 'valueA' + tagB: 'valueB' + } + vpnSiteLinks: [ + { + name: '<>-vSite-nvscom' + properties: { + bgpProperties: { + asn: 65010 + bgpPeeringAddress: '1.1.1.1' + } + ipAddress: '1.2.3.4' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } + { + name: 'Link1' + properties: { + bgpProperties: { + asn: 65020 + bgpPeeringAddress: '192.168.1.0' + } + ipAddress: '2.2.2.2' + linkProperties: { + linkProviderName: 'contoso' + linkSpeedInMbps: 5 + } + } + } + ] + } +} +``` + +
+

+ +

+ +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": "<>-nvscom" + }, + "virtualWanId": { + "value": "" + }, + // Non-required parameters + "deviceProperties": { + "value": { + "linkSpeedInMbps": 0 + } + }, + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "o365Policy": { + "value": { + "breakOutCategories": { + "allow": true, + "default": true, + "optimize": true + } + } + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "tagA": "valueA", + "tagB": "valueB" + } + }, + "vpnSiteLinks": { + "value": [ + { + "name": "<>-vSite-nvscom", + "properties": { + "bgpProperties": { + "asn": 65010, + "bgpPeeringAddress": "1.1.1.1" + }, + "ipAddress": "1.2.3.4", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + }, + { + "name": "Link1", + "properties": { + "bgpProperties": { + "asn": 65020, + "bgpPeeringAddress": "192.168.1.0" + }, + "ipAddress": "2.2.2.2", + "linkProperties": { + "linkProviderName": "contoso", + "linkSpeedInMbps": 5 + } + } + } + ] + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module vpnSites './Microsoft.Network/vpnSites/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-nvsmin' + params: { + // Required parameters + name: '<>-nvsmin' + virtualWanId: '' + // Non-required parameters + addressPrefixes: [ + '10.0.0.0/16' + ] + enableDefaultTelemetry: '' + ipAddress: '1.2.3.4' + } +} +``` + +
+

+ +

+ +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": "<>-nvsmin" + }, + "virtualWanId": { + "value": "" + }, + // Non-required parameters + "addressPrefixes": { + "value": [ + "10.0.0.0/16" + ] + }, + "enableDefaultTelemetry": { + "value": "" + }, + "ipAddress": { + "value": "1.2.3.4" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Network/vpnSites/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..28cecaa27 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,76 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Data Purger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '150f5e0c-0603-4f03-8c7f-cf70034c4e90') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: last(split(resourceId, '/'))! +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(logAnalyticsWorkspace.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } + scope: logAnalyticsWorkspace +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/dependencies.bicep new file mode 100644 index 000000000..fea9a507c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/dependencies.bicep @@ -0,0 +1,85 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Automation Account to create.') +param automationAccountName string + +@description('Required. The name of the Event Hub Workspace to create.') +param eventHubNamespaceName string + +@description('Required. The name of the Event Hub to create.') +param eventHubName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = { + name: automationAccountName + location: location + properties: { + sku: { + name: 'Basic' + } + } +} + +resource eventHubNamespace 'Microsoft.EventHub/namespaces@2022-10-01-preview' = { + name: eventHubNamespaceName + location: location + sku: { + name: 'Basic' + tier: 'Basic' + capacity: 1 + } + properties: { + minimumTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + disableLocalAuth: false + isAutoInflateEnabled: false + maximumThroughputUnits: 0 + kafkaEnabled: false + zoneRedundant: true + } + + resource eventHub 'eventhubs@2022-10-01-preview' = { + name: eventHubName + properties: { + messageRetentionInDays: 1 + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Automation Account.') +output automationAccountResourceId string = automationAccount.id + +@description('The resource ID of the created Eventhub Namespace.') +output eventHubNamespaceResourceId string = eventHubNamespace.id + +@description('The name of the created Eventhub.') +output eventHubName string = eventHubNamespace::eventHub.name + +@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/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/deploy.test.bicep new file mode 100644 index 000000000..b70b033bf --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/adv/deploy.test.bicep @@ -0,0 +1,291 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.operationalinsights.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'oiwadv' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + storageAccountName: 'dep<>sa${serviceShort}' + automationAccountName: 'dep-<>-auto-${serviceShort}' + eventHubNamespaceName: 'dep-<>-ehw-${serviceShort}' + eventHubName: 'dep-<>-eh-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + dailyQuotaGb: 10 + dataSources: [ + { + eventLogName: 'Application' + eventTypes: [ + { + eventType: 'Error' + } + { + eventType: 'Warning' + } + { + eventType: 'Information' + } + ] + kind: 'WindowsEvent' + name: 'applicationEvent' + } + { + counterName: '% Processor Time' + instanceName: '*' + intervalSeconds: 60 + kind: 'WindowsPerformanceCounter' + name: 'windowsPerfCounter1' + objectName: 'Processor' + } + { + kind: 'IISLogs' + name: 'sampleIISLog1' + state: 'OnPremiseEnabled' + } + { + kind: 'LinuxSyslog' + name: 'sampleSyslog1' + syslogName: 'kern' + syslogSeverities: [ + { + severity: 'emerg' + } + { + severity: 'alert' + } + { + severity: 'crit' + } + { + severity: 'err' + } + { + severity: 'warning' + } + ] + } + { + kind: 'LinuxSyslogCollection' + name: 'sampleSyslogCollection1' + state: 'Enabled' + } + { + instanceName: '*' + intervalSeconds: 10 + kind: 'LinuxPerformanceObject' + name: 'sampleLinuxPerf1' + objectName: 'Logical Disk' + syslogSeverities: [ + { + counterName: '% Used Inodes' + } + { + counterName: 'Free Megabytes' + } + { + counterName: '% Used Space' + } + { + counterName: 'Disk Transfers/sec' + } + { + counterName: 'Disk Reads/sec' + } + { + counterName: 'Disk Writes/sec' + } + ] + } + { + kind: 'LinuxPerformanceCollection' + name: 'sampleLinuxPerfCollection1' + state: 'Enabled' + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + gallerySolutions: [ + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + linkedServices: [ + { + name: 'Automation' + resourceId: nestedDependencies.outputs.automationAccountResourceId + } + ] + linkedStorageAccounts: [ + { + name: 'Query' + resourceId: nestedDependencies.outputs.storageAccountResourceId + } + ] + lock: 'CanNotDelete' + publicNetworkAccessForIngestion: 'Disabled' + publicNetworkAccessForQuery: 'Disabled' + savedSearches: [ + { + category: 'VDC Saved Searches' + displayName: 'VMSS Instance Count2' + name: 'VMSSQueries' + query: 'Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer' + } + ] + storageInsightsConfigs: [ + { + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + tables: [ + 'LinuxsyslogVer2v0' + 'WADETWEventTable' + 'WADServiceFabric*EventTable' + 'WADWindowsEventLogsTable' + ] + } + ] + useResourcePermissions: true + tables: [ + { + name: 'CustomTableBasic_CL' + schema: { + name: 'CustomTableBasic_CL' + columns: [ + { + name: 'TimeGenerated' + type: 'DateTime' + } + { + name: 'RawData' + type: 'String' + } + ] + } + totalRetentionInDays: 90 + retentionInDays: 60 + } + { + name: 'CustomTableAdvanced_CL' + schema: { + name: 'CustomTableAdvanced_CL' + columns: [ + { + name: 'TimeGenerated' + type: 'DateTime' + } + { + name: 'EventTime' + type: 'DateTime' + } + { + name: 'EventLevel' + type: 'String' + } + { + name: 'EventCode' + type: 'Int' + } + { + name: 'Message' + type: 'String' + } + { + name: 'RawData' + type: 'String' + } + ] + } + } + ] + dataExports: [ + { + name: 'eventHubExport' + enable: true + destination: { + resourceId: nestedDependencies.outputs.eventHubNamespaceResourceId + metaData: { + eventHubName: nestedDependencies.outputs.eventHubName + } + } + tableNames: [ + 'Alert' + 'InsightsMetrics' + ] + } + { + name: 'storageAccountExport' + enable: true + destination: { + resourceId: nestedDependencies.outputs.storageAccountResourceId + } + tableNames: [ + 'Operation' + ] + } + ] + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/dependencies.bicep new file mode 100644 index 000000000..8f83c0d9a --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/dependencies.bicep @@ -0,0 +1,47 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Storage Account to create.') +param storageAccountName string + +@description('Required. The name of the Automation Account to create.') +param automationAccountName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' = { + name: automationAccountName + location: location + properties: { + sku: { + name: 'Basic' + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +@description('The resource ID of the created Storage Account.') +output storageAccountResourceId string = storageAccount.id + +@description('The resource ID of the created Automation Account.') +output automationAccountResourceId string = automationAccount.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 diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/deploy.test.bicep new file mode 100644 index 000000000..6f6770c88 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/common/deploy.test.bicep @@ -0,0 +1,218 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.operationalinsights.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'oiwcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-nestedDependencies' + params: { + storageAccountName: 'dep<>sa${serviceShort}' + automationAccountName: 'dep-<>-auto-${serviceShort}' + managedIdentityName: 'dep-<>-msi-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../.shared/.templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-diagnosticDependencies' + params: { + storageAccountName: 'dep<>diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-<>-evh-${serviceShort}' + eventHubNamespaceName: 'dep-<>-evhns-${serviceShort}' + location: location + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + dailyQuotaGb: 10 + dataSources: [ + { + eventLogName: 'Application' + eventTypes: [ + { + eventType: 'Error' + } + { + eventType: 'Warning' + } + { + eventType: 'Information' + } + ] + kind: 'WindowsEvent' + name: 'applicationEvent' + } + { + counterName: '% Processor Time' + instanceName: '*' + intervalSeconds: 60 + kind: 'WindowsPerformanceCounter' + name: 'windowsPerfCounter1' + objectName: 'Processor' + } + { + kind: 'IISLogs' + name: 'sampleIISLog1' + state: 'OnPremiseEnabled' + } + { + kind: 'LinuxSyslog' + name: 'sampleSyslog1' + syslogName: 'kern' + syslogSeverities: [ + { + severity: 'emerg' + } + { + severity: 'alert' + } + { + severity: 'crit' + } + { + severity: 'err' + } + { + severity: 'warning' + } + ] + } + { + kind: 'LinuxSyslogCollection' + name: 'sampleSyslogCollection1' + state: 'Enabled' + } + { + instanceName: '*' + intervalSeconds: 10 + kind: 'LinuxPerformanceObject' + name: 'sampleLinuxPerf1' + objectName: 'Logical Disk' + syslogSeverities: [ + { + counterName: '% Used Inodes' + } + { + counterName: 'Free Megabytes' + } + { + counterName: '% Used Space' + } + { + counterName: 'Disk Transfers/sec' + } + { + counterName: 'Disk Reads/sec' + } + { + counterName: 'Disk Writes/sec' + } + ] + } + { + kind: 'LinuxPerformanceCollection' + name: 'sampleLinuxPerfCollection1' + state: 'Enabled' + } + ] + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: diagnosticDependencies.outputs.storageAccountResourceId + diagnosticWorkspaceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + diagnosticEventHubAuthorizationRuleId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + diagnosticEventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + gallerySolutions: [ + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + linkedServices: [ + { + name: 'Automation' + resourceId: nestedDependencies.outputs.automationAccountResourceId + } + ] + linkedStorageAccounts: [ + { + name: 'Query' + resourceId: nestedDependencies.outputs.storageAccountResourceId + } + ] + lock: 'CanNotDelete' + publicNetworkAccessForIngestion: 'Disabled' + publicNetworkAccessForQuery: 'Disabled' + savedSearches: [ + { + category: 'VDC Saved Searches' + displayName: 'VMSS Instance Count2' + name: 'VMSSQueries' + query: 'Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer' + } + ] + storageInsightsConfigs: [ + { + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + tables: [ + 'LinuxsyslogVer2v0' + 'WADETWEventTable' + 'WADServiceFabric*EventTable' + 'WADWindowsEventLogsTable' + ] + } + ] + useResourcePermissions: true + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + systemAssignedIdentity: true + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/min/deploy.test.bicep new file mode 100644 index 000000000..c6b49da06 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/.test/min/deploy.test.bicep @@ -0,0 +1,42 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.operationalinsights.workspaces-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'oiwmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/deploy.bicep new file mode 100644 index 000000000..d8122d3c7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/deploy.bicep @@ -0,0 +1,66 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The data export rule name.') +@minLength(4) +@maxLength(63) +param name string + +@description('Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Optional. Destination properties.') +param destination object = {} + +@description('Optional. Active when enabled.') +param enable bool = false + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. An array of tables to export, for example: [\'Heartbeat\', \'SecurityEvent\'].') +param tableNames array = [] + +// =============== // +// Deployments // +// =============== // + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: workspaceName +} + +resource dataExport 'Microsoft.OperationalInsights/workspaces/dataExports@2020-08-01' = { + parent: workspace + name: name + properties: { + destination: destination + enable: enable + tableNames: tableNames + } +} + +// =========== // +// Outputs // +// =========== // + +@description('The name of the data export.') +output name string = dataExport.name + +@description('The resource ID of the data export.') +output resourceId string = dataExport.id + +@description('The name of the resource group the data export was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/readme.md new file mode 100644 index 000000000..95685dead --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/readme.md @@ -0,0 +1,52 @@ +# Log Analytics Workspace Data Exports `[Microsoft.OperationalInsights/workspaces/dataExports]` + +This module deploys Log Analytics Workspace Data Exports. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/dataExports` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataExports) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The data export rule name. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `workspaceName` | string | The name of the parent workspaces. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `destination` | object | `{object}` | Destination properties. | +| `enable` | bool | `False` | Active when enabled. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `tableNames` | array | `[]` | An array of tables to export, for example: ['Heartbeat', 'SecurityEvent']. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the data export. | +| `resourceGroupName` | string | The name of the resource group the data export was created in. | +| `resourceId` | string | The resource ID of the data export. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataExports/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep new file mode 100644 index 000000000..aa5f54b37 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/deploy.bicep @@ -0,0 +1,102 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the solution.') +param name string + +@description('Required. The kind of the DataSource.') +@allowed([ + 'AzureActivityLog' + 'WindowsEvent' + 'WindowsPerformanceCounter' + 'IISLogs' + 'LinuxSyslog' + 'LinuxSyslogCollection' + 'LinuxPerformanceObject' + 'LinuxPerformanceCollection' +]) +param kind string = 'AzureActivityLog' + +@description('Optional. Tags to configure in the resource.') +param tags object = {} + +@description('Optional. Resource ID of the resource to be linked.') +param linkedResourceId string = '' + +@description('Optional. Windows event log name to configure when kind is WindowsEvent.') +param eventLogName string = '' + +@description('Optional. Windows event types to configure when kind is WindowsEvent.') +param eventTypes array = [] + +@description('Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject.') +param objectName string = '' + +@description('Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject.') +param instanceName string = '*' + +@description('Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject.') +param intervalSeconds int = 60 + +@description('Optional. List of counters to configure when the kind is LinuxPerformanceObject.') +param performanceCounters array = [] + +@description('Optional. Counter name to configure when kind is WindowsPerformanceCounter.') +param counterName string = '' + +@description('Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection.') +param state string = '' + +@description('Optional. System log to configure when kind is LinuxSyslog.') +param syslogName string = '' + +@description('Optional. Severities to configure when kind is LinuxSyslog.') +param syslogSeverities array = [] + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource dataSource 'Microsoft.OperationalInsights/workspaces/dataSources@2020-08-01' = { + name: name + parent: workspace + kind: kind + tags: tags + properties: { + linkedResourceId: !empty(kind) && kind == 'AzureActivityLog' ? linkedResourceId : null + eventLogName: !empty(kind) && kind == 'WindowsEvent' ? eventLogName : null + eventTypes: !empty(kind) && kind == 'WindowsEvent' ? eventTypes : null + objectName: !empty(kind) && (kind == 'WindowsPerformanceCounter' || kind == 'LinuxPerformanceObject') ? objectName : null + instanceName: !empty(kind) && (kind == 'WindowsPerformanceCounter' || kind == 'LinuxPerformanceObject') ? instanceName : null + intervalSeconds: !empty(kind) && (kind == 'WindowsPerformanceCounter' || kind == 'LinuxPerformanceObject') ? intervalSeconds : null + counterName: !empty(kind) && kind == 'WindowsPerformanceCounter' ? counterName : null + state: !empty(kind) && (kind == 'IISLogs' || kind == 'LinuxSyslogCollection' || kind == 'LinuxPerformanceCollection') ? state : null + syslogName: !empty(kind) && kind == 'LinuxSyslog' ? syslogName : null + syslogSeverities: !empty(kind) && (kind == 'LinuxSyslog' || kind == 'LinuxPerformanceObject') ? syslogSeverities : null + performanceCounters: !empty(kind) && kind == 'LinuxPerformanceObject' ? performanceCounters : null + } +} + +@description('The resource ID of the deployed data source.') +output resourceId string = dataSource.id + +@description('The resource group where the data source is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed data source.') +output name string = dataSource.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/readme.md new file mode 100644 index 000000000..678c0ade9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/readme.md @@ -0,0 +1,103 @@ +# Operationalinsights Workspaces Datasources `[Microsoft.OperationalInsights/workspaces/dataSources]` + +This template deploys a data source for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `kind` | string | `'AzureActivityLog'` | `[AzureActivityLog, IISLogs, LinuxPerformanceCollection, LinuxPerformanceObject, LinuxSyslog, LinuxSyslogCollection, WindowsEvent, WindowsPerformanceCounter]` | The kind of the DataSource. | +| `name` | string | | | Name of the solution. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `counterName` | string | `''` | Counter name to configure when kind is WindowsPerformanceCounter. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `eventLogName` | string | `''` | Windows event log name to configure when kind is WindowsEvent. | +| `eventTypes` | array | `[]` | Windows event types to configure when kind is WindowsEvent. | +| `instanceName` | string | `'*'` | Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject. | +| `intervalSeconds` | int | `60` | Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject. | +| `linkedResourceId` | string | `''` | Resource ID of the resource to be linked. | +| `objectName` | string | `''` | Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject. | +| `performanceCounters` | array | `[]` | List of counters to configure when the kind is LinuxPerformanceObject. | +| `state` | string | `''` | State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection. | +| `syslogName` | string | `''` | System log to configure when kind is LinuxSyslog. | +| `syslogSeverities` | array | `[]` | Severities to configure when kind is LinuxSyslog. | +| `tags` | object | `{object}` | Tags to configure in the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed data source. | +| `resourceGroupName` | string | The resource group where the data source is deployed. | +| `resourceId` | string | The resource ID of the deployed data source. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/dataSources/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/deploy.bicep new file mode 100644 index 000000000..a120025ca --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/deploy.bicep @@ -0,0 +1,367 @@ +@description('Required. Name of the Log Analytics workspace.') +param name string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Service Tier: PerGB2018, Free, Standalone, PerGB or PerNode.') +@allowed([ + 'Free' + 'Standalone' + 'PerNode' + 'PerGB2018' +]) +param serviceTier string = 'PerGB2018' + +@description('Optional. List of storage accounts to be read by the workspace.') +param storageInsightsConfigs array = [] + +@description('Optional. List of services to be linked.') +param linkedServices array = [] + +@description('Conditional. List of Storage Accounts to be linked. Required if \'forceCmkForQuery\' is set to \'true\' and \'savedSearches\' is not empty.') +param linkedStorageAccounts array = [] + +@description('Optional. Kusto Query Language searches to save.') +param savedSearches array = [] + +@description('Optional. LAW data export instances to be deployed.') +param dataExports array = [] + +@description('Optional. LAW data sources to configure.') +param dataSources array = [] + +@description('Optional. LAW custom tables to be deployed.') +param tables array = [] + +@description('Optional. List of gallerySolutions to be created in the log analytics workspace.') +param gallerySolutions array = [] + +@description('Optional. Number of days data will be retained for.') +@minValue(0) +@maxValue(730) +param dataRetention int = 365 + +@description('Optional. The workspace daily quota for ingestion.') +@minValue(-1) +param dailyQuotaGb int = -1 + +@description('Optional. The network access type for accessing Log Analytics ingestion.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForIngestion string = 'Enabled' + +@description('Optional. The network access type for accessing Log Analytics query.') +@allowed([ + 'Enabled' + 'Disabled' +]) +param publicNetworkAccessForQuery string = 'Enabled' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Set to \'true\' to use resource or workspace permissions and \'false\' (or leave empty) to require workspace permissions.') +param useResourcePermissions bool = false + +@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 diagnosticLogsRetentionInDays int = 365 + +@description('Optional. Resource ID of the diagnostic storage account.') +param diagnosticStorageAccountId string = '' + +@description('Optional. Resource ID of a log analytics workspace.') +param diagnosticWorkspaceId 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.') +param diagnosticEventHubAuthorizationRuleId 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.') +param diagnosticEventHubName string = '' + +@description('Optional. Indicates whether customer managed storage is mandatory for query management.') +param forceCmkForQuery bool = true + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource.') +@allowed([ + 'allLogs' + 'Audit' +]) +param diagnosticLogCategoriesToEnable array = [ + 'allLogs' +] + +@description('Optional. The name of metrics that will be streamed.') +@allowed([ + 'AllMetrics' +]) +param diagnosticMetricsToEnable array = [ + 'AllMetrics' +] + +@description('Optional. The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings".') +param diagnosticSettingsName string = '' + +var diagnosticsLogsSpecified = [for category in filter(diagnosticLogCategoriesToEnable, item => item != 'allLogs'): { + category: category + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var diagnosticsLogs = contains(diagnosticLogCategoriesToEnable, 'allLogs') ? [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } + } +] : diagnosticsLogsSpecified + +var diagnosticsMetrics = [for metric in diagnosticMetricsToEnable: { + category: metric + timeGrain: null + enabled: true + retentionPolicy: { + enabled: true + days: diagnosticLogsRetentionInDays + } +}] + +var logAnalyticsSearchVersion = 1 + +var enableReferencedModulesTelemetry = false + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : (!empty(userAssignedIdentities) ? 'UserAssigned' : 'None') + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + location: location + name: name + tags: tags + properties: { + features: { + searchVersion: logAnalyticsSearchVersion + enableLogAccessUsingOnlyResourcePermissions: useResourcePermissions + } + sku: { + name: serviceTier + } + retentionInDays: dataRetention + workspaceCapping: { + dailyQuotaGb: dailyQuotaGb + } + publicNetworkAccessForIngestion: publicNetworkAccessForIngestion + publicNetworkAccessForQuery: publicNetworkAccessForQuery + forceCmkForQuery: forceCmkForQuery + } + identity: identity +} + +resource logAnalyticsWorkspace_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if ((!empty(diagnosticStorageAccountId)) || (!empty(diagnosticWorkspaceId)) || (!empty(diagnosticEventHubAuthorizationRuleId)) || (!empty(diagnosticEventHubName))) { + name: !empty(diagnosticSettingsName) ? diagnosticSettingsName : '${name}-diagnosticSettings' + properties: { + storageAccountId: !empty(diagnosticStorageAccountId) ? diagnosticStorageAccountId : null + workspaceId: !empty(diagnosticWorkspaceId) ? diagnosticWorkspaceId : null + eventHubAuthorizationRuleId: !empty(diagnosticEventHubAuthorizationRuleId) ? diagnosticEventHubAuthorizationRuleId : null + eventHubName: !empty(diagnosticEventHubName) ? diagnosticEventHubName : null + metrics: diagnosticsMetrics + logs: diagnosticsLogs + } + scope: logAnalyticsWorkspace +} + +module logAnalyticsWorkspace_storageInsightConfigs 'storageInsightConfigs/deploy.bicep' = [for (storageInsightsConfig, index) in storageInsightsConfigs: { + name: '${uniqueString(deployment().name, location)}-LAW-StorageInsightsConfig-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + containers: contains(storageInsightsConfig, 'containers') ? storageInsightsConfig.containers : [] + tables: contains(storageInsightsConfig, 'tables') ? storageInsightsConfig.tables : [] + storageAccountResourceId: storageInsightsConfig.storageAccountResourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_linkedServices 'linkedServices/deploy.bicep' = [for (linkedService, index) in linkedServices: { + name: '${uniqueString(deployment().name, location)}-LAW-LinkedService-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: linkedService.name + resourceId: contains(linkedService, 'resourceId') ? linkedService.resourceId : '' + writeAccessResourceId: contains(linkedService, 'writeAccessResourceId') ? linkedService.writeAccessResourceId : '' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_linkedStorageAccounts 'linkedStorageAccounts/deploy.bicep' = [for (linkedStorageAccount, index) in linkedStorageAccounts: { + name: '${uniqueString(deployment().name, location)}-LAW-LinkedStorageAccount-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: linkedStorageAccount.name + resourceId: linkedStorageAccount.resourceId + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_savedSearches 'savedSearches/deploy.bicep' = [for (savedSearch, index) in savedSearches: { + name: '${uniqueString(deployment().name, location)}-LAW-SavedSearch-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: '${savedSearch.name}${uniqueString(deployment().name)}' + etag: contains(savedSearch, 'eTag') ? savedSearch.etag : '*' + displayName: savedSearch.displayName + category: savedSearch.category + query: savedSearch.query + functionAlias: contains(savedSearch, 'functionAlias') ? savedSearch.functionAlias : '' + functionParameters: contains(savedSearch, 'functionParameters') ? savedSearch.functionParameters : '' + version: contains(savedSearch, 'version') ? savedSearch.version : 2 + enableDefaultTelemetry: enableReferencedModulesTelemetry + } + dependsOn: [ + logAnalyticsWorkspace_linkedStorageAccounts + ] +}] + +module logAnalyticsWorkspace_dataExports 'dataExports/deploy.bicep' = [for (dataExport, index) in dataExports: { + name: '${uniqueString(deployment().name, location)}-LAW-DataExport-${index}' + params: { + workspaceName: logAnalyticsWorkspace.name + name: dataExport.name + destination: contains(dataExport, 'destination') ? dataExport.destination : {} + enable: contains(dataExport, 'enable') ? dataExport.enable : false + tableNames: contains(dataExport, 'tableNames') ? dataExport.tableNames : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_dataSources 'dataSources/deploy.bicep' = [for (dataSource, index) in dataSources: { + name: '${uniqueString(deployment().name, location)}-LAW-DataSource-${index}' + params: { + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + name: dataSource.name + kind: dataSource.kind + linkedResourceId: contains(dataSource, 'linkedResourceId') ? dataSource.linkedResourceId : '' + eventLogName: contains(dataSource, 'eventLogName') ? dataSource.eventLogName : '' + eventTypes: contains(dataSource, 'eventTypes') ? dataSource.eventTypes : [] + objectName: contains(dataSource, 'objectName') ? dataSource.objectName : '' + instanceName: contains(dataSource, 'instanceName') ? dataSource.instanceName : '' + intervalSeconds: contains(dataSource, 'intervalSeconds') ? dataSource.intervalSeconds : 60 + counterName: contains(dataSource, 'counterName') ? dataSource.counterName : '' + state: contains(dataSource, 'state') ? dataSource.state : '' + syslogName: contains(dataSource, 'syslogName') ? dataSource.syslogName : '' + syslogSeverities: contains(dataSource, 'syslogSeverities') ? dataSource.syslogSeverities : [] + performanceCounters: contains(dataSource, 'performanceCounters') ? dataSource.performanceCounters : [] + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_tables 'tables/deploy.bicep' = [for (table, index) in tables: { + name: '${uniqueString(deployment().name, location)}-LAW-Table-${index}' + params: { + workspaceName: logAnalyticsWorkspace.name + name: table.name + plan: contains(table, 'plan') ? table.plan : 'Analytics' + schema: contains(table, 'schema') ? table.schema : {} + retentionInDays: contains(table, 'retentionInDays') ? table.retentionInDays : -1 + totalRetentionInDays: contains(table, 'totalRetentionInDays') ? table.totalRetentionInDays : -1 + restoredLogs: contains(table, 'restoredLogs') ? table.restoredLogs : {} + searchResults: contains(table, 'searchResults') ? table.searchResults : {} + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +module logAnalyticsWorkspace_solutions '../../Microsoft.OperationsManagement/solutions/deploy.bicep' = [for (gallerySolution, index) in gallerySolutions: if (!empty(gallerySolutions)) { + name: '${uniqueString(deployment().name, location)}-LAW-Solution-${index}' + params: { + name: gallerySolution.name + location: location + logAnalyticsWorkspaceName: logAnalyticsWorkspace.name + product: contains(gallerySolution, 'product') ? gallerySolution.product : 'OMSGallery' + publisher: contains(gallerySolution, 'publisher') ? gallerySolution.publisher : 'Microsoft' + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +}] + +resource logAnalyticsWorkspace_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${logAnalyticsWorkspace.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: logAnalyticsWorkspace +} + +module logAnalyticsWorkspace_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-LAW-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + resourceId: logAnalyticsWorkspace.id + } +}] + +@description('The resource ID of the deployed log analytics workspace.') +output resourceId string = logAnalyticsWorkspace.id + +@description('The resource group of the deployed log analytics workspace.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed log analytics workspace.') +output name string = logAnalyticsWorkspace.name + +@description('The ID associated with the workspace.') +output logAnalyticsWorkspaceId string = logAnalyticsWorkspace.properties.customerId + +@description('The location the resource was deployed into.') +output location string = logAnalyticsWorkspace.location + +@description('The principal ID of the system assigned identity.') +output systemAssignedIdentityPrincipalId string = systemAssignedIdentity && contains(logAnalyticsWorkspace.identity, 'principalId') ? logAnalyticsWorkspace.identity.principalId : '' diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep new file mode 100644 index 000000000..8ed547b71 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/deploy.bicep @@ -0,0 +1,52 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the link.') +param name string + +@description('Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access.') +param resourceId string = '' + +@description('Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access.') +param writeAccessResourceId string = '' + +@description('Optional. Tags to configure in the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource linkedService 'Microsoft.OperationalInsights/workspaces/linkedServices@2020-08-01' = { + name: name + parent: workspace + tags: tags + properties: { + resourceId: resourceId + writeAccessResourceId: empty(writeAccessResourceId) ? null : writeAccessResourceId + } +} + +@description('The name of the deployed linked service.') +output name string = linkedService.name + +@description('The resource ID of the deployed linked service.') +output resourceId string = linkedService.id + +@description('The resource group where the linked service is deployed.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md new file mode 100644 index 000000000..f39fb2d75 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/readme.md @@ -0,0 +1,93 @@ +# Operationalinsights Workspaces Linked Services `[Microsoft.OperationalInsights/workspaces/linkedServices]` + +This template deploys a linked service for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | | Name of the link. | +| `resourceId` | string | `''` | The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `tags` | object | `{object}` | Tags to configure in the resource. | +| `writeAccessResourceId` | string | `''` | The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed linked service. | +| `resourceGroupName` | string | The resource group where the linked service is deployed. | +| `resourceId` | string | The resource ID of the deployed linked service. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedServices/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep new file mode 100644 index 000000000..e2564b196 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/deploy.bicep @@ -0,0 +1,52 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the link.') +@allowed([ + 'Query' + 'Alerts' + 'CustomLogs' + 'AzureWatson' +]) +param name string + +@description('Required. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access.') +param resourceId string + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource linkedStorageAccount 'Microsoft.OperationalInsights/workspaces/linkedStorageAccounts@2020-08-01' = { + name: name + parent: workspace + properties: { + storageAccountIds: [ + resourceId + ] + } +} + +@description('The name of the deployed linked storage account.') +output name string = linkedStorageAccount.name + +@description('The resource ID of the deployed linked storage account.') +output resourceId string = linkedStorageAccount.id + +@description('The resource group where the linked storage account is deployed.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md new file mode 100644 index 000000000..48ed3b750 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/readme.md @@ -0,0 +1,50 @@ +# Operational Insights Workspaces Linked Storage Accounts `[Microsoft.OperationalInsights/workspaces/linkedStorageAccounts]` + +This template deploys a linked Storage Accounts for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Allowed Values | Description | +| :-- | :-- | :-- | :-- | +| `name` | string | `[Alerts, AzureWatson, CustomLogs, Query]` | Name of the link. | +| `resourceId` | string | | The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed linked storage account. | +| `resourceGroupName` | string | The resource group where the linked storage account is deployed. | +| `resourceId` | string | The resource ID of the deployed linked storage account. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/linkedStorageAccounts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/readme.md new file mode 100644 index 000000000..1ff9e83c1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/readme.md @@ -0,0 +1,1468 @@ +# Log Analytics Workspaces `[Microsoft.OperationalInsights/workspaces]` + +This template deploys a log analytics workspace. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.OperationalInsights/workspaces` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces) | +| `Microsoft.OperationalInsights/workspaces/dataExports` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataExports) | +| `Microsoft.OperationalInsights/workspaces/dataSources` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/dataSources) | +| `Microsoft.OperationalInsights/workspaces/linkedServices` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedServices) | +| `Microsoft.OperationalInsights/workspaces/linkedStorageAccounts` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/linkedStorageAccounts) | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | +| `Microsoft.OperationalInsights/workspaces/tables` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces/tables) | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Name of the Log Analytics workspace. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `linkedStorageAccounts` | _[linkedStorageAccounts](linkedStorageAccounts/readme.md)_ array | List of Storage Accounts to be linked. Required if 'forceCmkForQuery' is set to 'true' and 'savedSearches' is not empty. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `dailyQuotaGb` | int | `-1` | | The workspace daily quota for ingestion. | +| `dataExports` | _[dataExports](dataExports/readme.md)_ array | `[]` | | LAW data export instances to be deployed. | +| `dataRetention` | int | `365` | | Number of days data will be retained for. | +| `dataSources` | _[dataSources](dataSources/readme.md)_ array | `[]` | | LAW data sources to configure. | +| `diagnosticEventHubAuthorizationRuleId` | 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. | +| `diagnosticEventHubName` | 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. | +| `diagnosticLogCategoriesToEnable` | array | `[allLogs]` | `[allLogs, Audit]` | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. | +| `diagnosticLogsRetentionInDays` | int | `365` | | Specifies the number of days that logs will be kept for; a value of 0 will retain data indefinitely. | +| `diagnosticMetricsToEnable` | array | `[AllMetrics]` | `[AllMetrics]` | The name of metrics that will be streamed. | +| `diagnosticSettingsName` | string | `''` | | The name of the diagnostic setting, if deployed. If left empty, it defaults to "-diagnosticSettings". | +| `diagnosticStorageAccountId` | string | `''` | | Resource ID of the diagnostic storage account. | +| `diagnosticWorkspaceId` | string | `''` | | Resource ID of a log analytics workspace. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `forceCmkForQuery` | bool | `True` | | Indicates whether customer managed storage is mandatory for query management. | +| `gallerySolutions` | array | `[]` | | List of gallerySolutions to be created in the log analytics workspace. | +| `linkedServices` | _[linkedServices](linkedServices/readme.md)_ array | `[]` | | List of services to be linked. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `publicNetworkAccessForIngestion` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Log Analytics ingestion. | +| `publicNetworkAccessForQuery` | string | `'Enabled'` | `[Disabled, Enabled]` | The network access type for accessing Log Analytics query. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `savedSearches` | _[savedSearches](savedSearches/readme.md)_ array | `[]` | | Kusto Query Language searches to save. | +| `serviceTier` | string | `'PerGB2018'` | `[Free, PerGB2018, PerNode, Standalone]` | Service Tier: PerGB2018, Free, Standalone, PerGB or PerNode. | +| `storageInsightsConfigs` | array | `[]` | | List of storage accounts to be read by the workspace. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tables` | _[tables](tables/readme.md)_ array | `[]` | | LAW custom tables to be deployed. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | +| `useResourcePermissions` | bool | `False` | | Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions. | + + +### Parameter Usage: `gallerySolutions` + +Ref cross-referenced _[solutions](../../Microsoft.OperationsManagement/solutions/readme.md)_ + +

+ +Parameter JSON format + +```json +"gallerySolutions": { + "value": [ + { + "name": "AgentHealthAssessment", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AlertManagement", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AntiMalware", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureActivity", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureAutomation", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureCdnCoreAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureDataFactoryAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureNSGAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "AzureSQLAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "ChangeTracking", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "Containers", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "InfrastructureInsights", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "KeyVaultAnalytics", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "LogicAppsManagement", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "NetworkMonitoring", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "Security", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "SecurityCenterFree", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "ServiceFabric", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "ServiceMap", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "SQLAssessment", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "Updates", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "VMInsights", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "WireData2", + "product": "OMSGallery", + "publisher": "Microsoft" + }, + { + "name": "WaaSUpdateInsights", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +gallerySolutions: [ + { + name: 'AgentHealthAssessment' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AlertManagement' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AntiMalware' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureActivity' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureCdnCoreAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureDataFactoryAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureNSGAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'AzureSQLAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'ChangeTracking' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'Containers' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'InfrastructureInsights' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'KeyVaultAnalytics' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'LogicAppsManagement' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'NetworkMonitoring' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'Security' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'SecurityCenterFree' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'ServiceFabric' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'ServiceMap' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'SQLAssessment' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'Updates' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'VMInsights' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'WireData2' + product: 'OMSGallery' + publisher: 'Microsoft' + } + { + name: 'WaaSUpdateInsights' + product: 'OMSGallery' + publisher: 'Microsoft' + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `logAnalyticsWorkspaceId` | string | The ID associated with the workspace. | +| `name` | string | The name of the deployed log analytics workspace. | +| `resourceGroupName` | string | The resource group of the deployed log analytics workspace. | +| `resourceId` | string | The resource ID of the deployed log analytics workspace. | +| `systemAssignedIdentityPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.OperationsManagement/solutions` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Adv

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.OperationalInsights/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-oiwadv' + params: { + // Required parameters + name: '<>oiwadv001' + // Non-required parameters + dailyQuotaGb: 10 + dataExports: [ + { + destination: { + metaData: { + eventHubName: '' + } + resourceId: '' + } + enable: true + name: 'eventHubExport' + tableNames: [ + 'Alert' + 'InsightsMetrics' + ] + } + { + destination: { + resourceId: '' + } + enable: true + name: 'storageAccountExport' + tableNames: [ + 'Operation' + ] + } + ] + dataSources: [ + { + eventLogName: 'Application' + eventTypes: [ + { + eventType: 'Error' + } + { + eventType: 'Warning' + } + { + eventType: 'Information' + } + ] + kind: 'WindowsEvent' + name: 'applicationEvent' + } + { + counterName: '% Processor Time' + instanceName: '*' + intervalSeconds: 60 + kind: 'WindowsPerformanceCounter' + name: 'windowsPerfCounter1' + objectName: 'Processor' + } + { + kind: 'IISLogs' + name: 'sampleIISLog1' + state: 'OnPremiseEnabled' + } + { + kind: 'LinuxSyslog' + name: 'sampleSyslog1' + syslogName: 'kern' + syslogSeverities: [ + { + severity: 'emerg' + } + { + severity: 'alert' + } + { + severity: 'crit' + } + { + severity: 'err' + } + { + severity: 'warning' + } + ] + } + { + kind: 'LinuxSyslogCollection' + name: 'sampleSyslogCollection1' + state: 'Enabled' + } + { + instanceName: '*' + intervalSeconds: 10 + kind: 'LinuxPerformanceObject' + name: 'sampleLinuxPerf1' + objectName: 'Logical Disk' + syslogSeverities: [ + { + counterName: '% Used Inodes' + } + { + counterName: 'Free Megabytes' + } + { + counterName: '% Used Space' + } + { + counterName: 'Disk Transfers/sec' + } + { + counterName: 'Disk Reads/sec' + } + { + counterName: 'Disk Writes/sec' + } + ] + } + { + kind: 'LinuxPerformanceCollection' + name: 'sampleLinuxPerfCollection1' + state: 'Enabled' + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + gallerySolutions: [ + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + linkedServices: [ + { + name: 'Automation' + resourceId: '' + } + ] + linkedStorageAccounts: [ + { + name: 'Query' + resourceId: '' + } + ] + lock: 'CanNotDelete' + publicNetworkAccessForIngestion: 'Disabled' + publicNetworkAccessForQuery: 'Disabled' + savedSearches: [ + { + category: 'VDC Saved Searches' + displayName: 'VMSS Instance Count2' + name: 'VMSSQueries' + query: 'Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer' + } + ] + storageInsightsConfigs: [ + { + storageAccountResourceId: '' + tables: [ + 'LinuxsyslogVer2v0' + 'WADETWEventTable' + 'WADServiceFabric*EventTable' + 'WADWindowsEventLogsTable' + ] + } + ] + tables: [ + { + name: 'CustomTableBasic_CL' + retentionInDays: 60 + schema: { + columns: [ + { + name: 'TimeGenerated' + type: 'DateTime' + } + { + name: 'RawData' + type: 'String' + } + ] + name: 'CustomTableBasic_CL' + } + totalRetentionInDays: 90 + } + { + name: 'CustomTableAdvanced_CL' + schema: { + columns: [ + { + name: 'TimeGenerated' + type: 'DateTime' + } + { + name: 'EventTime' + type: 'DateTime' + } + { + name: 'EventLevel' + type: 'String' + } + { + name: 'EventCode' + type: 'Int' + } + { + name: 'Message' + type: 'String' + } + { + name: 'RawData' + type: 'String' + } + ] + name: 'CustomTableAdvanced_CL' + } + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + userAssignedIdentities: { + '': {} + } + useResourcePermissions: true + } +} +``` + +
+

+ +

+ +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": "<>oiwadv001" + }, + // Non-required parameters + "dailyQuotaGb": { + "value": 10 + }, + "dataExports": { + "value": [ + { + "destination": { + "metaData": { + "eventHubName": "" + }, + "resourceId": "" + }, + "enable": true, + "name": "eventHubExport", + "tableNames": [ + "Alert", + "InsightsMetrics" + ] + }, + { + "destination": { + "resourceId": "" + }, + "enable": true, + "name": "storageAccountExport", + "tableNames": [ + "Operation" + ] + } + ] + }, + "dataSources": { + "value": [ + { + "eventLogName": "Application", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + }, + { + "eventType": "Information" + } + ], + "kind": "WindowsEvent", + "name": "applicationEvent" + }, + { + "counterName": "% Processor Time", + "instanceName": "*", + "intervalSeconds": 60, + "kind": "WindowsPerformanceCounter", + "name": "windowsPerfCounter1", + "objectName": "Processor" + }, + { + "kind": "IISLogs", + "name": "sampleIISLog1", + "state": "OnPremiseEnabled" + }, + { + "kind": "LinuxSyslog", + "name": "sampleSyslog1", + "syslogName": "kern", + "syslogSeverities": [ + { + "severity": "emerg" + }, + { + "severity": "alert" + }, + { + "severity": "crit" + }, + { + "severity": "err" + }, + { + "severity": "warning" + } + ] + }, + { + "kind": "LinuxSyslogCollection", + "name": "sampleSyslogCollection1", + "state": "Enabled" + }, + { + "instanceName": "*", + "intervalSeconds": 10, + "kind": "LinuxPerformanceObject", + "name": "sampleLinuxPerf1", + "objectName": "Logical Disk", + "syslogSeverities": [ + { + "counterName": "% Used Inodes" + }, + { + "counterName": "Free Megabytes" + }, + { + "counterName": "% Used Space" + }, + { + "counterName": "Disk Transfers/sec" + }, + { + "counterName": "Disk Reads/sec" + }, + { + "counterName": "Disk Writes/sec" + } + ] + }, + { + "kind": "LinuxPerformanceCollection", + "name": "sampleLinuxPerfCollection1", + "state": "Enabled" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "gallerySolutions": { + "value": [ + { + "name": "AzureAutomation", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "linkedServices": { + "value": [ + { + "name": "Automation", + "resourceId": "" + } + ] + }, + "linkedStorageAccounts": { + "value": [ + { + "name": "Query", + "resourceId": "" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "publicNetworkAccessForIngestion": { + "value": "Disabled" + }, + "publicNetworkAccessForQuery": { + "value": "Disabled" + }, + "savedSearches": { + "value": [ + { + "category": "VDC Saved Searches", + "displayName": "VMSS Instance Count2", + "name": "VMSSQueries", + "query": "Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer" + } + ] + }, + "storageInsightsConfigs": { + "value": [ + { + "storageAccountResourceId": "", + "tables": [ + "LinuxsyslogVer2v0", + "WADETWEventTable", + "WADServiceFabric*EventTable", + "WADWindowsEventLogsTable" + ] + } + ] + }, + "tables": { + "value": [ + { + "name": "CustomTableBasic_CL", + "retentionInDays": 60, + "schema": { + "columns": [ + { + "name": "TimeGenerated", + "type": "DateTime" + }, + { + "name": "RawData", + "type": "String" + } + ], + "name": "CustomTableBasic_CL" + }, + "totalRetentionInDays": 90 + }, + { + "name": "CustomTableAdvanced_CL", + "schema": { + "columns": [ + { + "name": "TimeGenerated", + "type": "DateTime" + }, + { + "name": "EventTime", + "type": "DateTime" + }, + { + "name": "EventLevel", + "type": "String" + }, + { + "name": "EventCode", + "type": "Int" + }, + { + "name": "Message", + "type": "String" + }, + { + "name": "RawData", + "type": "String" + } + ], + "name": "CustomTableAdvanced_CL" + } + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "userAssignedIdentities": { + "value": { + "": {} + } + }, + "useResourcePermissions": { + "value": true + } + } +} +``` + +
+

+ +

Example 2: Common

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.OperationalInsights/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-oiwcom' + params: { + // Required parameters + name: '<>oiwcom001' + // Non-required parameters + dailyQuotaGb: 10 + dataSources: [ + { + eventLogName: 'Application' + eventTypes: [ + { + eventType: 'Error' + } + { + eventType: 'Warning' + } + { + eventType: 'Information' + } + ] + kind: 'WindowsEvent' + name: 'applicationEvent' + } + { + counterName: '% Processor Time' + instanceName: '*' + intervalSeconds: 60 + kind: 'WindowsPerformanceCounter' + name: 'windowsPerfCounter1' + objectName: 'Processor' + } + { + kind: 'IISLogs' + name: 'sampleIISLog1' + state: 'OnPremiseEnabled' + } + { + kind: 'LinuxSyslog' + name: 'sampleSyslog1' + syslogName: 'kern' + syslogSeverities: [ + { + severity: 'emerg' + } + { + severity: 'alert' + } + { + severity: 'crit' + } + { + severity: 'err' + } + { + severity: 'warning' + } + ] + } + { + kind: 'LinuxSyslogCollection' + name: 'sampleSyslogCollection1' + state: 'Enabled' + } + { + instanceName: '*' + intervalSeconds: 10 + kind: 'LinuxPerformanceObject' + name: 'sampleLinuxPerf1' + objectName: 'Logical Disk' + syslogSeverities: [ + { + counterName: '% Used Inodes' + } + { + counterName: 'Free Megabytes' + } + { + counterName: '% Used Space' + } + { + counterName: 'Disk Transfers/sec' + } + { + counterName: 'Disk Reads/sec' + } + { + counterName: 'Disk Writes/sec' + } + ] + } + { + kind: 'LinuxPerformanceCollection' + name: 'sampleLinuxPerfCollection1' + state: 'Enabled' + } + ] + diagnosticEventHubAuthorizationRuleId: '' + diagnosticEventHubName: '' + diagnosticLogsRetentionInDays: 7 + diagnosticStorageAccountId: '' + diagnosticWorkspaceId: '' + enableDefaultTelemetry: '' + gallerySolutions: [ + { + name: 'AzureAutomation' + product: 'OMSGallery' + publisher: 'Microsoft' + } + ] + linkedServices: [ + { + name: 'Automation' + resourceId: '' + } + ] + linkedStorageAccounts: [ + { + name: 'Query' + resourceId: '' + } + ] + lock: 'CanNotDelete' + publicNetworkAccessForIngestion: 'Disabled' + publicNetworkAccessForQuery: 'Disabled' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + savedSearches: [ + { + category: 'VDC Saved Searches' + displayName: 'VMSS Instance Count2' + name: 'VMSSQueries' + query: 'Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer' + } + ] + storageInsightsConfigs: [ + { + storageAccountResourceId: '' + tables: [ + 'LinuxsyslogVer2v0' + 'WADETWEventTable' + 'WADServiceFabric*EventTable' + 'WADWindowsEventLogsTable' + ] + } + ] + systemAssignedIdentity: true + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + useResourcePermissions: true + } +} +``` + +
+

+ +

+ +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": "<>oiwcom001" + }, + // Non-required parameters + "dailyQuotaGb": { + "value": 10 + }, + "dataSources": { + "value": [ + { + "eventLogName": "Application", + "eventTypes": [ + { + "eventType": "Error" + }, + { + "eventType": "Warning" + }, + { + "eventType": "Information" + } + ], + "kind": "WindowsEvent", + "name": "applicationEvent" + }, + { + "counterName": "% Processor Time", + "instanceName": "*", + "intervalSeconds": 60, + "kind": "WindowsPerformanceCounter", + "name": "windowsPerfCounter1", + "objectName": "Processor" + }, + { + "kind": "IISLogs", + "name": "sampleIISLog1", + "state": "OnPremiseEnabled" + }, + { + "kind": "LinuxSyslog", + "name": "sampleSyslog1", + "syslogName": "kern", + "syslogSeverities": [ + { + "severity": "emerg" + }, + { + "severity": "alert" + }, + { + "severity": "crit" + }, + { + "severity": "err" + }, + { + "severity": "warning" + } + ] + }, + { + "kind": "LinuxSyslogCollection", + "name": "sampleSyslogCollection1", + "state": "Enabled" + }, + { + "instanceName": "*", + "intervalSeconds": 10, + "kind": "LinuxPerformanceObject", + "name": "sampleLinuxPerf1", + "objectName": "Logical Disk", + "syslogSeverities": [ + { + "counterName": "% Used Inodes" + }, + { + "counterName": "Free Megabytes" + }, + { + "counterName": "% Used Space" + }, + { + "counterName": "Disk Transfers/sec" + }, + { + "counterName": "Disk Reads/sec" + }, + { + "counterName": "Disk Writes/sec" + } + ] + }, + { + "kind": "LinuxPerformanceCollection", + "name": "sampleLinuxPerfCollection1", + "state": "Enabled" + } + ] + }, + "diagnosticEventHubAuthorizationRuleId": { + "value": "" + }, + "diagnosticEventHubName": { + "value": "" + }, + "diagnosticLogsRetentionInDays": { + "value": 7 + }, + "diagnosticStorageAccountId": { + "value": "" + }, + "diagnosticWorkspaceId": { + "value": "" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "gallerySolutions": { + "value": [ + { + "name": "AzureAutomation", + "product": "OMSGallery", + "publisher": "Microsoft" + } + ] + }, + "linkedServices": { + "value": [ + { + "name": "Automation", + "resourceId": "" + } + ] + }, + "linkedStorageAccounts": { + "value": [ + { + "name": "Query", + "resourceId": "" + } + ] + }, + "lock": { + "value": "CanNotDelete" + }, + "publicNetworkAccessForIngestion": { + "value": "Disabled" + }, + "publicNetworkAccessForQuery": { + "value": "Disabled" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "savedSearches": { + "value": [ + { + "category": "VDC Saved Searches", + "displayName": "VMSS Instance Count2", + "name": "VMSSQueries", + "query": "Event | where Source == ServiceFabricNodeBootstrapAgent | summarize AggregatedValue = count() by Computer" + } + ] + }, + "storageInsightsConfigs": { + "value": [ + { + "storageAccountResourceId": "", + "tables": [ + "LinuxsyslogVer2v0", + "WADETWEventTable", + "WADServiceFabric*EventTable", + "WADWindowsEventLogsTable" + ] + } + ] + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "useResourcePermissions": { + "value": true + } + } +} +``` + +
+

+ +

Example 3: Min

+ +
+ +via Bicep module + +```bicep +module workspaces './Microsoft.OperationalInsights/workspaces/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-oiwmin' + params: { + // Required parameters + name: '<>oiwmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>oiwmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep new file mode 100644 index 000000000..4e5fe5ce6 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/deploy.bicep @@ -0,0 +1,73 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Required. Name of the saved search.') +param name string + +@description('Required. Display name for the search.') +param displayName string + +@description('Required. Query category.') +param category string + +@description('Required. Kusto Query to be stored.') +param query string + +@description('Optional. Tags to configure in the resource.') +param tags array = [] + +@description('Optional. The function alias if query serves as a function.') +param functionAlias string = '' + +@description('Optional. The optional function parameters if query serves as a function. Value should be in the following format: "param-name1:type1 = default_value1, param-name2:type2 = default_value2". For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions.') +param functionParameters string = '' + +@description('Optional. The version number of the query language.') +param version int = 2 + +@description('Optional. The ETag of the saved search. To override an existing saved search, use "*" or specify the current Etag.') +param etag string = '*' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource savedSearch 'Microsoft.OperationalInsights/workspaces/savedSearches@2020-08-01' = { + name: name + parent: workspace + //etag: etag // According to API, the variable should be here, but it doesn't work here. + properties: { + etag: etag + tags: tags + displayName: displayName + category: category + query: query + functionAlias: functionAlias + functionParameters: functionParameters + version: version + } +} + +@description('The resource ID of the deployed saved search.') +output resourceId string = savedSearch.id + +@description('The resource group where the saved search is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployed saved search.') +output name string = savedSearch.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md new file mode 100644 index 000000000..3ca7b5adb --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/readme.md @@ -0,0 +1,98 @@ +# Operationalinsights Workspaces Saved Searches `[Microsoft.OperationalInsights/workspaces/savedSearches]` + +This template deploys a saved search for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/savedSearches` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/savedSearches) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `category` | string | Query category. | +| `displayName` | string | Display name for the search. | +| `name` | string | Name of the saved search. | +| `query` | string | Kusto Query to be stored. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `etag` | string | `'*'` | The ETag of the saved search. To override an existing saved search, use "*" or specify the current Etag. | +| `functionAlias` | string | `''` | The function alias if query serves as a function. | +| `functionParameters` | string | `''` | The optional function parameters if query serves as a function. Value should be in the following format: "param-name1:type1 = default_value1, param-name2:type2 = default_value2". For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions. | +| `tags` | array | `[]` | Tags to configure in the resource. | +| `version` | int | `2` | The version number of the query language. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the deployed saved search. | +| `resourceGroupName` | string | The resource group where the saved search is deployed. | +| `resourceId` | string | The resource ID of the deployed saved search. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/savedSearches/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep new file mode 100644 index 000000000..3ac3b852c --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/deploy.bicep @@ -0,0 +1,63 @@ +@description('Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment.') +param logAnalyticsWorkspaceName string + +@description('Optional. The name of the storage insights config.') +param name string = '${last(split(storageAccountResourceId, '/'))}-stinsconfig' + +@description('Required. The Azure Resource Manager ID of the storage account resource.') +param storageAccountResourceId string + +@description('Optional. The names of the blob containers that the workspace should read.') +param containers array = [] + +@description('Optional. The names of the Azure tables that the workspace should read.') +param tables array = [] + +@description('Optional. Tags to configure in the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = { + name: last(split(storageAccountResourceId, '/'))! +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource storageinsightconfig 'Microsoft.OperationalInsights/workspaces/storageInsightConfigs@2020-08-01' = { + name: name + parent: workspace + tags: tags + properties: { + containers: containers + tables: tables + storageAccount: { + id: storageAccountResourceId + key: storageAccount.listKeys().keys[0].value + } + } +} + +@description('The resource ID of the deployed storage insights configuration.') +output resourceId string = storageinsightconfig.id + +@description('The resource group where the storage insight configuration is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the storage insights configuration.') +output name string = storageinsightconfig.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md new file mode 100644 index 000000000..1b2173071 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/readme.md @@ -0,0 +1,94 @@ +# Operationalinsights Workspaces Storage Insight Configs `[Microsoft.OperationalInsights/workspaces/storageInsightConfigs]` + +This template deploys a storage insights configuration for a Log Analytics workspace. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/storageInsightConfigs` | [2020-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2020-08-01/workspaces/storageInsightConfigs) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `storageAccountResourceId` | string | The Azure Resource Manager ID of the storage account resource. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `containers` | array | `[]` | The names of the blob containers that the workspace should read. | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `name` | string | `[format('{0}-stinsconfig', last(split(parameters('storageAccountResourceId'), '/')))]` | The name of the storage insights config. | +| `tables` | array | `[]` | The names of the Azure tables that the workspace should read. | +| `tags` | object | `{object}` | Tags to configure in the resource. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the storage insights configuration. | +| `resourceGroupName` | string | The resource group where the storage insight configuration is deployed. | +| `resourceId` | string | The resource ID of the deployed storage insights configuration. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/storageInsightConfigs/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/deploy.bicep new file mode 100644 index 000000000..85c5ff180 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/deploy.bicep @@ -0,0 +1,84 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the table.') +param name string + +@description('Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment.') +param workspaceName string + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +@description('Optional. Instruct the system how to handle and charge the logs ingested to this table.') +@allowed([ + 'Basic' + 'Analytics' +]) +param plan string = 'Analytics' + +@description('Optional. Restore parameters.') +param restoredLogs object = {} + +@description('Optional. The table retention in days, between 4 and 730. Setting this property to -1 will default to the workspace retention.') +@minValue(-1) +@maxValue(730) +param retentionInDays int = -1 + +@description('Optional. Table\'s schema.') +param schema object = {} + +@description('Optional. Parameters of the search job that initiated this table.') +param searchResults object = {} + +@description('Optional. The table total retention in days, between 4 and 2555. Setting this property to -1 will default to table retention.') +@minValue(-1) +@maxValue(2555) +param totalRetentionInDays int = -1 + +// =============== // +// Deployments // +// =============== // + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: workspaceName +} + +resource table 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' = { + parent: workspace + name: name + properties: { + plan: plan + restoredLogs: restoredLogs + retentionInDays: retentionInDays + schema: schema + searchResults: searchResults + totalRetentionInDays: totalRetentionInDays + } +} + +// =========== // +// Outputs // +// =========== // + +@description('The name of the table.') +output name string = table.name + +@description('The resource ID of the table.') +output resourceId string = table.id + +@description('The name of the resource group the table was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/readme.md new file mode 100644 index 000000000..40e503267 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/readme.md @@ -0,0 +1,55 @@ +# Log Analytics Workspace Tables `[Microsoft.OperationalInsights/workspaces/tables]` + +This module deploys Log Analytics Workspace Tables. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationalInsights/workspaces/tables` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2022-10-01/workspaces/tables) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the table. | + +**Conditional parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `workspaceName` | string | The name of the parent workspaces. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `plan` | string | `'Analytics'` | `[Analytics, Basic]` | Instruct the system how to handle and charge the logs ingested to this table. | +| `restoredLogs` | object | `{object}` | | Restore parameters. | +| `retentionInDays` | int | `-1` | | The table retention in days, between 4 and 730. Setting this property to -1 will default to the workspace retention. | +| `schema` | object | `{object}` | | Table's schema. | +| `searchResults` | object | `{object}` | | Parameters of the search job that initiated this table. | +| `totalRetentionInDays` | int | `-1` | | The table total retention in days, between 4 and 2555. Setting this property to -1 will default to table retention. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the table. | +| `resourceGroupName` | string | The name of the resource group the table was created in. | +| `resourceId` | string | The resource ID of the table. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/tables/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationalInsights/workspaces/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/dependencies.bicep new file mode 100644 index 000000000..ef3592fb5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The name of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceName string = logAnalytics.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/deploy.test.bicep new file mode 100644 index 000000000..b89c87d41 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/min/deploy.test.bicep @@ -0,0 +1,51 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.operationsmanagement.solutions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'omsmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: 'Updates' + logAnalyticsWorkspaceName: nestedDependencies.outputs.logAnalyticsWorkspaceName + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/dependencies.bicep new file mode 100644 index 000000000..ef3592fb5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The name of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceName string = logAnalytics.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/deploy.test.bicep new file mode 100644 index 000000000..1c7defeb3 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/ms/deploy.test.bicep @@ -0,0 +1,53 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.operationsmanagement.solutions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'omsms' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: 'AzureAutomation' + logAnalyticsWorkspaceName: nestedDependencies.outputs.logAnalyticsWorkspaceName + product: 'OMSGallery' + publisher: 'Microsoft' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/dependencies.bicep new file mode 100644 index 000000000..ef3592fb5 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/dependencies.bicep @@ -0,0 +1,13 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location +} + +@description('The name of the created Log Analytics Workspace.') +output logAnalyticsWorkspaceName string = logAnalytics.name diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/deploy.test.bicep new file mode 100644 index 000000000..e8188e90d --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/.test/nonms/deploy.test.bicep @@ -0,0 +1,53 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.operationsmanagement.solutions-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'omsnonms' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: { + logAnalyticsWorkspaceName: 'dep-<>-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + logAnalyticsWorkspaceName: nestedDependencies.outputs.logAnalyticsWorkspaceName + product: 'nonmsTestSolutionProduct' + publisher: 'nonmsTestSolutionPublisher' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/deploy.bicep new file mode 100644 index 000000000..a72a45070 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/deploy.bicep @@ -0,0 +1,63 @@ +@description('Required. Name of the solution. For Microsoft published gallery solution the target solution resource name will be composed as `{name}({logAnalyticsWorkspaceName})`.') +param name string + +@description('Required. Name of the Log Analytics workspace where the solution will be deployed/enabled.') +param logAnalyticsWorkspaceName string + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. The product of the deployed solution. For Microsoft published gallery solution it should be `OMSGallery` and the target solution resource product will be composed as `OMSGallery/{name}`. For third party solution, it can be anything. This is case sensitive.') +param product string = 'OMSGallery' + +@description('Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`.') +param publisher string = 'Microsoft' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +var solutionName = publisher == 'Microsoft' ? '${name}(${logAnalyticsWorkspace.name})' : name + +var solutionProduct = publisher == 'Microsoft' ? 'OMSGallery/${name}' : product + +resource solution 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = { + name: solutionName + location: location + properties: { + workspaceResourceId: logAnalyticsWorkspace.id + } + plan: { + name: solutionName + promotionCode: '' + product: solutionProduct + publisher: publisher + } +} + +@description('The name of the deployed solution.') +output name string = solution.name + +@description('The resource ID of the deployed solution.') +output resourceId string = solution.id + +@description('The resource group where the solution is deployed.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = solution.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/readme.md new file mode 100644 index 000000000..a7a21334b --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/readme.md @@ -0,0 +1,219 @@ +# OperationsManagement Solutions `[Microsoft.OperationsManagement/solutions]` + +This module deploys OperationsManagement Solutions. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.OperationsManagement/solutions` | [2015-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationsManagement/2015-11-01-preview/solutions) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `logAnalyticsWorkspaceName` | string | Name of the Log Analytics workspace where the solution will be deployed/enabled. | +| `name` | string | Name of the solution. For Microsoft published gallery solution the target solution resource name will be composed as `{name}({logAnalyticsWorkspaceName})`. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[resourceGroup().location]` | Location for all resources. | +| `product` | string | `'OMSGallery'` | The product of the deployed solution. For Microsoft published gallery solution it should be `OMSGallery` and the target solution resource product will be composed as `OMSGallery/{name}`. For third party solution, it can be anything. This is case sensitive. | +| `publisher` | string | `'Microsoft'` | The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`. | + + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployed solution. | +| `resourceGroupName` | string | The resource group where the solution is deployed. | +| `resourceId` | string | The resource ID of the deployed solution. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module solutions './Microsoft.OperationsManagement/solutions/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-omsmin' + params: { + // Required parameters + logAnalyticsWorkspaceName: '' + name: 'Updates' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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 + "logAnalyticsWorkspaceName": { + "value": "" + }, + "name": { + "value": "Updates" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

+ +

Example 2: Ms

+ +
+ +via Bicep module + +```bicep +module solutions './Microsoft.OperationsManagement/solutions/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-omsms' + params: { + // Required parameters + logAnalyticsWorkspaceName: '' + name: 'AzureAutomation' + // Non-required parameters + enableDefaultTelemetry: '' + product: 'OMSGallery' + publisher: 'Microsoft' + } +} +``` + +
+

+ +

+ +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 + "logAnalyticsWorkspaceName": { + "value": "" + }, + "name": { + "value": "AzureAutomation" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "product": { + "value": "OMSGallery" + }, + "publisher": { + "value": "Microsoft" + } + } +} +``` + +
+

+ +

Example 3: Nonms

+ +
+ +via Bicep module + +```bicep +module solutions './Microsoft.OperationsManagement/solutions/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-omsnonms' + params: { + // Required parameters + logAnalyticsWorkspaceName: '' + name: '<>omsnonms001' + // Non-required parameters + enableDefaultTelemetry: '' + product: 'nonmsTestSolutionProduct' + publisher: 'nonmsTestSolutionPublisher' + } +} +``` + +
+

+ +

+ +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 + "logAnalyticsWorkspaceName": { + "value": "" + }, + "name": { + "value": "<>omsnonms001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "product": { + "value": "nonmsTestSolutionProduct" + }, + "publisher": { + "value": "nonmsTestSolutionPublisher" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.OperationsManagement/solutions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/dependencies.bicep new file mode 100644 index 000000000..eb7f2fdc8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/dependencies.bicep @@ -0,0 +1,28 @@ +@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 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-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +@description('The resource ID of the created managed identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created storage account.') +output storageAccountResourceId string = storageAccount.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/deploy.test.bicep new file mode 100644 index 000000000..709559530 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/cli/deploy.test.bicep @@ -0,0 +1,78 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.resources.deploymentscripts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'rdscli' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + storageAccountName: 'dep<>st${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + azCliVersion: '2.40.0' + cleanupPreference: 'Always' + kind: 'AzureCLI' + retentionInterval: 'P1D' + runOnce: false + scriptContent: 'echo \'echo echo echo\'' + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + timeout: 'PT30M' + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + environmentVariables: { + secureList: [ + { + name: 'var1' + value: 'test' + } + { + name: 'var2' + secureValue: guid(deployment().name) + } + ] + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/dependencies.bicep new file mode 100644 index 000000000..eb7f2fdc8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/dependencies.bicep @@ -0,0 +1,28 @@ +@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 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-09-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' +} + +@description('The resource ID of the created managed identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created storage account.') +output storageAccountResourceId string = storageAccount.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/deploy.test.bicep new file mode 100644 index 000000000..4be1aa6c2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/.test/ps/deploy.test.bicep @@ -0,0 +1,67 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.resources.deploymentscripts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'rdsps' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + storageAccountName: 'dep<>st${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + azPowerShellVersion: '8.0' + cleanupPreference: 'Always' + kind: 'AzurePowerShell' + lock: 'CanNotDelete' + retentionInterval: 'P1D' + runOnce: false + scriptContent: 'Write-Host \'The cake is a lie!\'' + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + timeout: 'PT30M' + userAssignedIdentities: { + '${nestedDependencies.outputs.managedIdentityResourceId}': {} + } + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/deploy.bicep new file mode 100644 index 000000000..963bf7103 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/deploy.bicep @@ -0,0 +1,152 @@ +@description('Required. Display name of the script to be run.') +param name string + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Type of the script. AzurePowerShell, AzureCLI.') +@allowed([ + 'AzurePowerShell' + 'AzureCLI' +]) +param kind string = 'AzurePowerShell' + +@description('Optional. Azure PowerShell module version to be used.') +param azPowerShellVersion string = '3.0' + +@description('Optional. Azure CLI module version to be used.') +param azCliVersion string = '' + +@description('Optional. Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead.') +param scriptContent string = '' + +@description('Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent instead.') +param primaryScriptUri string = '' + +@description('Optional. The environment variables to pass over to the script. The list is passed as an object with a key name "secureList" and the value is the list of environment variables (array). The list must have a \'name\' and a \'value\' or a \'secretValue\' property for each object.') +@secure() +param environmentVariables object = {} + +@description('Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent).') +param supportingScriptUris array = [] + +@description('Optional. Command-line arguments to pass to the script. Arguments are separated by spaces.') +param arguments string = '' + +@description('Optional. Interval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P7D means one week).') +param retentionInterval string = 'P1D' + +@description('Optional. When set to false, script will run every time the template is deployed. When set to true, the script will only run once.') +param runOnce bool = false + +@description('Optional. The clean up preference when the script execution gets in a terminal state. Specify the preference on when to delete the deployment script resources. The default value is Always, which means the deployment script resources are deleted despite the terminal state (Succeeded, Failed, canceled).') +@allowed([ + 'Always' + 'OnSuccess' + 'OnExpiration' +]) +param cleanupPreference string = 'Always' + +@description('Optional. Container group name, if not specified then the name will get auto-generated. Not specifying a \'containerGroupName\' indicates the system to generate a unique name which might end up flagging an Azure Policy as non-compliant. Use \'containerGroupName\' when you have an Azure Policy that expects a specific naming convention or when you want to fully control the name. \'containerGroupName\' property must be between 1 and 63 characters long, must contain only lowercase letters, numbers, and dashes and it cannot start or end with a dash and consecutive dashes are not allowed.') +param containerGroupName string = '' + +@description('Optional. The resource ID of the storage account to use for this deployment script. If none is provided, the deployment script uses a temporary, managed storage account.') +param storageAccountResourceId string = '' + +@description('Optional. Maximum allowed script execution time specified in ISO 8601 format. Default value is PT1H - 1 hour; \'PT30M\' - 30 minutes; \'P5D\' - 5 days; \'P1Y\' 1 year.') +param timeout string = 'PT1H' + +@description('Generated. Do not provide a value! This date value is used to make sure the script run every time the template is deployed.') +param baseTime string = utcNow('yyyy-MM-dd-HH-mm-ss') + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var containerSettings = { + containerGroupName: containerGroupName +} + +var identityType = !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = identityType != 'None' ? { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} : null + +var storageAccountSettings = !empty(storageAccountResourceId) ? { + storageAccountKey: listKeys(storageAccountResourceId, '2019-06-01').keys[0].value + storageAccountName: last(split(storageAccountResourceId, '/')) +} : {} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: name + location: location + tags: tags + identity: identity + kind: any(kind) + properties: { + azPowerShellVersion: kind == 'AzurePowerShell' ? azPowerShellVersion : null + azCliVersion: kind == 'AzureCLI' ? azCliVersion : null + containerSettings: !empty(containerGroupName) ? containerSettings : null + storageAccountSettings: !empty(storageAccountResourceId) ? storageAccountSettings : null + arguments: arguments + environmentVariables: !empty(environmentVariables) ? environmentVariables.secureList : [] + scriptContent: !empty(scriptContent) ? scriptContent : null + primaryScriptUri: !empty(primaryScriptUri) ? primaryScriptUri : null + supportingScriptUris: !empty(supportingScriptUris) ? supportingScriptUris : null + cleanupPreference: cleanupPreference + forceUpdateTag: runOnce ? resourceGroup().name : baseTime + retentionInterval: retentionInterval + timeout: timeout + } +} + +resource deploymentScript_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock)) { + name: '${deploymentScript.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: deploymentScript +} + +@description('The resource ID of the deployment script.') +output resourceId string = deploymentScript.id + +@description('The resource group the deployment script was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the deployment script.') +output name string = deploymentScript.name + +@description('The location the resource was deployed into.') +output location string = deploymentScript.location + +@description('The output of the deployment script.') +output outputs object = contains(deploymentScript.properties, 'outputs') ? deploymentScript.properties.outputs : {} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/readme.md new file mode 100644 index 000000000..502422283 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/readme.md @@ -0,0 +1,377 @@ +# Deployment Scripts `[Microsoft.Resources/deploymentScripts]` + +This module deploys a deployment script. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Considerations](#Considerations) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Resources/deploymentScripts` | [2020-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2020-10-01/deploymentScripts) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | Display name of the script to be run. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `arguments` | string | `''` | | Command-line arguments to pass to the script. Arguments are separated by spaces. | +| `azCliVersion` | string | `''` | | Azure CLI module version to be used. | +| `azPowerShellVersion` | string | `'3.0'` | | Azure PowerShell module version to be used. | +| `cleanupPreference` | string | `'Always'` | `[Always, OnExpiration, OnSuccess]` | The clean up preference when the script execution gets in a terminal state. Specify the preference on when to delete the deployment script resources. The default value is Always, which means the deployment script resources are deleted despite the terminal state (Succeeded, Failed, canceled). | +| `containerGroupName` | string | `''` | | Container group name, if not specified then the name will get auto-generated. Not specifying a 'containerGroupName' indicates the system to generate a unique name which might end up flagging an Azure Policy as non-compliant. Use 'containerGroupName' when you have an Azure Policy that expects a specific naming convention or when you want to fully control the name. 'containerGroupName' property must be between 1 and 63 characters long, must contain only lowercase letters, numbers, and dashes and it cannot start or end with a dash and consecutive dashes are not allowed. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `environmentVariables` | secureObject | `{object}` | | The environment variables to pass over to the script. The list is passed as an object with a key name "secureList" and the value is the list of environment variables (array). The list must have a 'name' and a 'value' or a 'secretValue' property for each object. | +| `kind` | string | `'AzurePowerShell'` | `[AzureCLI, AzurePowerShell]` | Type of the script. AzurePowerShell, AzureCLI. | +| `location` | string | `[resourceGroup().location]` | | Location for all resources. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `primaryScriptUri` | string | `''` | | Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent instead. | +| `retentionInterval` | string | `'P1D'` | | Interval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P7D means one week). | +| `runOnce` | bool | `False` | | When set to false, script will run every time the template is deployed. When set to true, the script will only run once. | +| `scriptContent` | string | `''` | | Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead. | +| `storageAccountResourceId` | string | `''` | | The resource ID of the storage account to use for this deployment script. If none is provided, the deployment script uses a temporary, managed storage account. | +| `supportingScriptUris` | array | `[]` | | List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent). | +| `tags` | object | `{object}` | | Tags of the resource. | +| `timeout` | string | `'PT1H'` | | Maximum allowed script execution time specified in ISO 8601 format. Default value is PT1H - 1 hour; 'PT30M' - 30 minutes; 'P5D' - 5 days; 'P1Y' 1 year. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + +**Generated parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `baseTime` | string | `[utcNow('yyyy-MM-dd-HH-mm-ss')]` | Do not provide a value! This date value is used to make sure the script run every time the template is deployed. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/<>/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the deployment script. | +| `outputs` | object | The output of the deployment script. | +| `resourceGroupName` | string | The resource group the deployment script was deployed into. | +| `resourceId` | string | The resource ID of the deployment script. | + +## Considerations + +This module requires a User Assigned Identity (MSI, managed service identity) to exist, and this MSI has to have contributor rights on the subscription - that allows the Deployment Script to create the required Storage Account and the Azure Container Instance. + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Cli

+ +
+ +via Bicep module + +```bicep +module deploymentScripts './Microsoft.Resources/deploymentScripts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-rdscli' + params: { + // Required parameters + name: '<>rdscli001' + // Non-required parameters + azCliVersion: '2.40.0' + cleanupPreference: 'Always' + enableDefaultTelemetry: '' + environmentVariables: { + secureList: [ + { + name: 'var1' + value: 'test' + } + { + name: 'var2' + secureValue: '' + } + ] + } + kind: 'AzureCLI' + retentionInterval: 'P1D' + runOnce: false + scriptContent: 'echo \'echo echo echo\'' + storageAccountResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + timeout: 'PT30M' + userAssignedIdentities: { + '': {} + } + } +} +``` + +
+

+ +

+ +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": "<>rdscli001" + }, + // Non-required parameters + "azCliVersion": { + "value": "2.40.0" + }, + "cleanupPreference": { + "value": "Always" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "environmentVariables": { + "value": { + "secureList": [ + { + "name": "var1", + "value": "test" + }, + { + "name": "var2", + "secureValue": "" + } + ] + } + }, + "kind": { + "value": "AzureCLI" + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": false + }, + "scriptContent": { + "value": "echo \"echo echo echo\"" + }, + "storageAccountResourceId": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "timeout": { + "value": "PT30M" + }, + "userAssignedIdentities": { + "value": { + "": {} + } + } + } +} +``` + +
+

+ +

Example 2: Ps

+ +
+ +via Bicep module + +```bicep +module deploymentScripts './Microsoft.Resources/deploymentScripts/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-rdsps' + params: { + // Required parameters + name: '<>rdsps001' + // Non-required parameters + azPowerShellVersion: '8.0' + cleanupPreference: 'Always' + enableDefaultTelemetry: '' + kind: 'AzurePowerShell' + lock: 'CanNotDelete' + retentionInterval: 'P1D' + runOnce: false + scriptContent: 'Write-Host \'The cake is a lie!\'' + storageAccountResourceId: '' + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + timeout: 'PT30M' + userAssignedIdentities: { + '': {} + } + } +} +``` + +
+

+ +

+ +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": "<>rdsps001" + }, + // Non-required parameters + "azPowerShellVersion": { + "value": "8.0" + }, + "cleanupPreference": { + "value": "Always" + }, + "enableDefaultTelemetry": { + "value": "" + }, + "kind": { + "value": "AzurePowerShell" + }, + "lock": { + "value": "CanNotDelete" + }, + "retentionInterval": { + "value": "P1D" + }, + "runOnce": { + "value": false + }, + "scriptContent": { + "value": "Write-Host \"The cake is a lie!\"" + }, + "storageAccountResourceId": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + }, + "timeout": { + "value": "PT30M" + }, + "userAssignedIdentities": { + "value": { + "": {} + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/version.json new file mode 100644 index 000000000..56f8d9ca4 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/deploymentScripts/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.4" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep new file mode 100644 index 000000000..47f3db6f9 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.bicep/nested_roleAssignments.bicep @@ -0,0 +1,248 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string = resourceGroup().id + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +@sys.description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') +param condition string = '' + +@sys.description('Optional. Version of the condition.') +@allowed([ + '2.0' +]) +param conditionVersion string = '2.0' + +@sys.description('Optional. Id of the delegated managed identity resource.') +param delegatedManagedIdentityResourceId string = '' + +var builtInRoleNames = { + 'API Management Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '312a565d-c81f-4fd8-895a-4e21e48d571c') + 'API Management Service Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e022efe7-f5ba-4159-bbe4-b44f577e9b61') + 'API Management Service Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '71522526-b88f-4d52-b57f-d31fc3546d0d') + 'Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ca6382a4-1721-4bcf-a114-ff0c70227b6b') + 'Application Insights Component Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e') + 'Application Insights Snapshot Debugger': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b') + 'Automation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f353d9bd-d4a6-484e-a77a-8050b599b867') + 'Automation Job Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4fe576fe-1146-4730-92eb-48519fa6bf9f') + 'Automation Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd3881f73-407a-4167-8283-e981cbba0404') + 'Automation Runbook Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5fb5aef8-1081-4b8e-bb16-9d5d0385bab5') + 'Autonomous Development Platform Data Contributor (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b8b15564-4fa6-4a59-ab12-03e1d9594795') + 'Autonomous Development Platform Data Owner (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '27f8b550-c507-4db9-86f2-f4b8e816d59d') + 'Autonomous Development Platform Data Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd63b75f7-47ea-4f27-92ac-e0d173aaf093') + 'Avere Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4f8fab4f-1852-4a58-a46a-8eaf358af14a') + 'Avere Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c025889f-8102-4ebf-b32c-fc0c6f0c6bd9') + 'Azure Arc Enabled Kubernetes Cluster User Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00493d72-78f6-4148-b6c5-d3ce8e4799dd') + 'Azure Arc Kubernetes Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dffb1e0c-446f-4dde-a09f-99eb5cc68b96') + 'Azure Arc Kubernetes Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8393591c-06b9-48a2-a542-1bd6b377f6a2') + 'Azure Arc Kubernetes Viewer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63f0a09d-1495-4db4-a681-037d84835eb4') + 'Azure Arc Kubernetes Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5b999177-9696-4545-85c7-50de3797e5a1') + 'Azure Arc ScVmm Administrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a92dfd61-77f9-4aec-a531-19858b406c87') + 'Azure Arc ScVmm Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c0781e91-8102-4553-8951-97c6d4243cda') + 'Azure Arc ScVmm Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6aac74c4-6311-40d2-bbdd-7d01e7c6e3a9') + 'Azure Arc ScVmm VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e582369a-e17b-42a5-b10c-874c387c530b') + 'Azure Arc VMware Administrator role ': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ddc140ed-e463-4246-9145-7c664192013f') + 'Azure Arc VMware Private Cloud User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce551c02-7c42-47e0-9deb-e3b6fc3a9a83') + 'Azure Arc VMware Private Clouds Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '67d33e57-3129-45e6-bb0b-7cc522f762fa') + 'Azure Arc VMware VM Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b748a06d-6150-4f8a-aaa9-ce3940cd96cb') + 'Azure Center for SAP solutions administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7b0c7e81-271f-4c71-90bf-e30bdfdbc2f7') + 'Azure Center for SAP solutions reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '05352d14-a920-4328-a0de-4cbe7430e26b') + 'Azure Center for SAP solutions service role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aabbc5dd-1af0-458b-a942-81af88f9c138') + 'Azure Connected Machine Resource Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cd570a14-e51a-42ad-bac8-bafd67325302') + 'Azure Extension for SQL Server Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7392c568-9289-4bde-aaaa-b7131215889d') + 'Azure Front Door Domain Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0ab34830-df19-4f8c-b84e-aa85b8afa6e8') + 'Azure Front Door Domain Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f99d363-226e-4dca-9920-b807cf8e1a5f') + 'Azure Front Door Secret Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3f2eb865-5811-4578-b90a-6fc6fa0df8e5') + 'Azure Front Door Secret Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0db238c4-885e-4c4f-a933-aa2cef684fca') + 'Azure Kubernetes Fleet Manager Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63bb64ad-9799-4770-b5c3-24ed299a07bf') + 'Azure Kubernetes Fleet Manager RBAC Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '434fb43a-c01c-447e-9f67-c3ad923cfaba') + 'Azure Kubernetes Fleet Manager RBAC Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ab4d3d-a1bf-4477-8ad9-8359bc988f69') + 'Azure Kubernetes Fleet Manager RBAC Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '30b27cfc-9c84-438e-b0ce-70e35255df80') + 'Azure Kubernetes Fleet Manager RBAC Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5af6afb3-c06c-4fa4-8848-71a8aee05683') + 'Azure Kubernetes Service Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8') + 'Azure Kubernetes Service Policy Add-on Deployment': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ed5180-3e48-46fd-8541-4ea054d57064') + 'Azure Kubernetes Service RBAC Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3498e952-d568-435e-9b2c-8d77e338d7f7') + 'Azure Kubernetes Service RBAC Cluster Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b') + 'Azure Kubernetes Service RBAC Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f6c6a51-bcf8-42ba-9220-52d62157d7db') + 'Azure Kubernetes Service RBAC Writer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb') + 'Azure Maps Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dba33070-676a-4fb0-87fa-064dc56ff7fb') + 'Azure Stack HCI registration role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bda0d508-adf1-4af0-9c28-88919fc3ae06') + 'Azure Traffic Controller Configuration Manager': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbc52c3f-28ad-4303-a892-8a056630b8f1') + 'Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e467623-bb1f-42f4-a55d-6e525e11384b') + 'Backup Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00c29273-979b-4161-815c-10b084fb9324') + 'BizTalk Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e3c6656-6cfa-4708-81fe-0de47ac73342') + 'Blueprint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '41077137-e803-4205-871c-5a86e6a753b4') + 'Blueprint Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '437d2ced-4a38-4302-8479-ed2bcb43d090') + 'CDN Endpoint Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '426e0c7f-0c7e-4658-b36f-ff54d6c29b45') + 'CDN Endpoint Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '871e35f6-b5c1-49cc-a043-bde969a0f2cd') + 'CDN Profile Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ec156ff8-a8d1-4d15-830c-5b80698ca432') + 'CDN Profile Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8f96442b-4075-438f-813d-ad51ab4019af') + 'Chamber Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4e9b8407-af2e-495b-ae54-bb60a55b1b5a') + 'Chamber User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4447db05-44ed-4da3-ae60-6cbece780e32') + 'Classic Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b34d265f-36f7-4a0d-a4d4-e158ca92e90f') + 'Classic Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86e8f5dc-a6e9-4c67-9d15-de283e8eac25') + 'Classic Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb') + 'ClearDB MySQL DB Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9106cda0-8a86-4e81-b686-29a22c54effe') + 'Code Signing Certificate Profile Signer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2837e146-70d7-4cfd-ad55-7efa6464f958') + 'Cognitive Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68') + 'Cognitive Services User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') + 'Collaborative Data Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'daa9e50b-21df-454c-94a6-a8050adab352') + 'Collaborative Runtime Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7a6f0e70-c033-4fb1-828c-08514e5f4102') + 'ContainerApp Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b') + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Cosmos DB Account Reader Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8') + 'Cosmos DB Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa') + 'Cost Management Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '434105ed-43f6-45c7-a02f-909b2ba83430') + 'Cost Management Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '72fafb9e-0641-4937-9268-a91bfd8191a3') + 'Data Box Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'add466c9-e687-43fc-8d98-dfcf8d720be5') + 'Data Factory Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '673868aa-7521-48a0-acc6-0f60742d39f5') + 'Data Lake Analytics Developer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '47b7735b-770e-4598-a7da-8b91488b4c88') + 'Deployment Environments User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e40d4e-8d2e-438d-97e1-9528336e149c') + 'Desktop Virtualization Application Group Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') + 'Desktop Virtualization Application Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aebf23d0-b568-4e86-b8f9-fe83a2c6ab55') + 'Desktop Virtualization Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') + 'Desktop Virtualization Host Pool Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e307426c-f9b6-4e81-87de-d99efb3c32bc') + 'Desktop Virtualization Host Pool Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ceadfde2-b300-400a-ab7b-6143895aa822') + 'Desktop Virtualization Power On Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33') + 'Desktop Virtualization Power On Off Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + 'Desktop Virtualization Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49a72310-ab8d-41df-bbb0-79b649203868') + 'Desktop Virtualization Session Host Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') + 'Desktop Virtualization User Session Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ea4bfff8-7fb4-485a-aadd-d4129a0ffaa6') + 'Desktop Virtualization Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') + 'Desktop Virtualization Workspace Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21efdde3-836f-432b-bf3d-3e8e734d4b2b') + 'Desktop Virtualization Workspace Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0fa44ee9-7a7d-466b-9bb2-2bf446b1204d') + 'DevCenter Dev Box User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45d50f46-0b78-4001-a660-4198cbe8cd05') + 'DevCenter Project Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '331c37c6-af14-46d9-b9f4-e1909e1b95a0') + 'Device Update Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '02ca0879-e8e4-47a5-a61e-5c618b76e64a') + 'Device Update Content Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0378884a-3af5-44ab-8323-f5b22f9f3c98') + 'Device Update Content Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd1ee9a80-8b14-47f0-bdc2-f4a351625a7b') + 'Device Update Deployments Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4237640-0e3d-4a46-8fda-70bc94856432') + 'Device Update Deployments Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '49e2f5d2-7741-4835-8efa-19e1fe35e47f') + 'Device Update Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e9dba6fb-3d52-4cf0-bce3-f06ce71b9e0f') + 'DevTest Labs User': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64') + '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') + 'DNS Resolver Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d') + 'DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314') + 'DocumentDB Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450') + 'Domain Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2') + 'Domain Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb') + 'Elastic SAN Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '80dcbedb-47ef-405d-95bd-188a1b4ac406') + 'Elastic SAN Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'af6a70f8-3c9f-4105-acf1-d719e9fca4ca') + 'EventGrid Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1e241071-0855-49ea-94dc-649edcd759de') + 'EventGrid Data Sender': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a91429-5739-47e2-a06b-3470a27159e7') + 'EventGrid EventSubscription Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '428e0ff0-5e57-4d9c-a221-2c70d0e0a443') + 'EventGrid EventSubscription Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2414bbcf-6497-4faf-8c65-045460748405') + 'Experimentation Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f646f1b-fa08-80eb-a33b-edd6ce5c915c') + 'Experimentation Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f646f1b-fa08-80eb-a22b-edd6ce5c915c') + 'Guest Configuration Resource Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '088ab73d-1256-47ae-bea9-9de8e7131f31') + 'HDInsight Cluster Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '61ed4efc-fab3-44fd-b111-e24485cc132a') + 'Intelligent Systems Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '03a6d094-3444-4b3d-88af-7477090a9e5e') + 'Key Vault Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + 'Key Vault Certificates Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4417e6f-fecd-4de8-b567-7b0420556985') + 'Key Vault Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f25e0fa2-a7c8-4377-a976-54943a77a395') + 'Key Vault Crypto Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '14b46e9e-c2b7-41b4-b07b-48a6ebf60603') + 'Key Vault Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2') + 'Key Vault Secrets Officer': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b86a8fe4-44ce-4948-aee5-eccb2c155cd7') + 'Kubernetes Cluster - Azure Arc Onboarding': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '34e09817-6cbe-4d01-b1a2-e0eac5743d41') + 'Kubernetes Extension Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '85cb6faf-e071-4c9b-8136-154b5a04f717') + 'Lab Assistant': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ce40b423-cede-4313-a93f-9b28290b72e1') + 'Lab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5daaa2af-1fe8-407c-9122-bba179798270') + 'Lab Creator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b97fb8bc-a8b2-4522-a38b-dd33c7e65ead') + 'Lab Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a36e6959-b6be-4b12-8e9f-ef4b474d304d') + 'Lab Services Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f69b8690-cc87-41d6-b77a-a4bc3c0a966f') + 'Lab Services Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a5c394f-5eb7-4d4f-9c8e-e8eae39faebc') + 'Load Test Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749a398d-560b-491b-bb21-08924219302e') + 'Load Test Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '45bb0b16-2f0c-4e78-afaa-a07599b003f6') + 'Load Test Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3ae3fb29-0000-4ccd-bf80-542e7b26e081') + 'LocalNGFirewallAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a8835c7d-b5cb-47fa-b6f0-65ea10ce07a2') + 'LocalRulestacksAdministrator role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfc3b73d-c6ff-45eb-9a5f-40298295bf20') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Logic App Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '87a39d53-fc1b-424a-814c-f7e04687dc9e') + 'Logic App Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Managed Identity Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59') + 'Managed Identity Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830') + 'Media Services Account Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '054126f8-9a2b-4f1c-a9ad-eca461f08466') + 'Media Services Live Events Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '532bc159-b25e-42c0-969e-a1d439f60d77') + 'Media Services Media Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e4395492-1534-4db2-bedf-88c14621589c') + 'Media Services Policy Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c4bba371-dacd-4a26-b320-7250bca963ae') + 'Media Services Streaming Endpoints Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '99dba123-b5fe-44d5-874c-ced7199a5804') + 'Microsoft Sentinel Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ab8e14d6-4a74-4a29-9ba8-549422addade') + 'Microsoft Sentinel Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8d289c81-5878-46d4-8554-54e1e3d8b5cb') + 'Microsoft Sentinel Responder': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e150937-b8fe-4cfb-8069-0eaf05ecd056') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Network Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7') + 'New Relic APM Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5d28c62d-5b37-4476-8438-e587778df237') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'PlayFab Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c8b84dc-067c-4039-9615-fa1a4b77c726') + 'PlayFab Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a9a19cc5-31f4-447c-901f-56c0bb18fcaf') + 'Private DNS Zone Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f') + 'Quota Request Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Redis Cache Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e0f68234-74aa-48ed-b826-c38b57376e17') + 'Reservation Purchaser': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') + 'Scheduler Job Collections Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '188a0f2f-5c9e-469b-ae67-2aa5ce574b94') + 'Search Service Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0') + 'Security Admin': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd') + 'Security Manager (Legacy)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e3d13bf0-dd5a-482e-ba6b-9b8433878d10') + 'Security Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4') + 'Services Hub Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '82200a5b-e217-47a5-b665-6d8765ee745b') + 'SignalR AccessKey Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'Site Recovery Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6670b86e-a3f7-4917-ac9b-5d6ab1be4567') + 'Site Recovery Operator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '494ae006-db33-4328-bf46-533a6560a3ca') + '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') + 'Storage Account Backup Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1') + 'Storage Account Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab') + 'Support Request Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cfd33db0-3dd1-45e3-aa9d-cdbdf3b6f24e') + 'Tag Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4a9ae827-6dc8-4573-8ac7-8239d42aa03f') + 'Template Spec Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c9b6475-caf0-4164-b5a1-2142a7116f4b') + 'Template Spec Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '392ae280-861d-42bd-9ea5-08ee6d83b80e') + 'Traffic Manager Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a4b10055-b0c7-44c2-b00f-c7b5b3550cf7') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Virtual Machine Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c') + 'Web Plan Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b') + 'Website Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: { + name: guid(resourceId, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + condition: !empty(condition) ? condition : null + conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + } +}] diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep new file mode 100644 index 000000000..9a8446268 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/dependencies.bicep @@ -0,0 +1,18 @@ +@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 + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep new file mode 100644 index 000000000..5a6f8e1a2 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/common/deploy.test.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.resources.resourcegroups-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'rrgcom' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +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: 'dep-<>-msi-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + lock: 'CanNotDelete' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + nestedDependencies.outputs.managedIdentityPrincipalId + ] + principalType: 'ServicePrincipal' + } + ] + tags: { + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep new file mode 100644 index 000000000..33ff6731f --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/.test/min/deploy.test.bicep @@ -0,0 +1,23 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'rrgmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + name: '<>${serviceShort}001' + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/deploy.bicep new file mode 100644 index 000000000..8a3421479 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/deploy.bicep @@ -0,0 +1,79 @@ +targetScope = 'subscription' + +@description('Required. The name of the Resource Group.') +param name string + +@description('Optional. Location of the Resource Group. It uses the deployment\'s location when not provided.') +param location string = deployment().location + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the storage account resource.') +param tags object = {} + +@description('Optional. The ID of the resource that manages this resource group.') +param managedBy string = '' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + location: location + name: name + tags: tags + managedBy: managedBy + properties: {} +} + +module resourceGroup_lock '../../Microsoft.Authorization/locks/resourceGroup/deploy.bicep' = if (!empty(lock)) { + name: '${uniqueString(deployment().name, location)}-${lock}-Lock' + params: { + level: any(lock) + name: '${resourceGroup.name}-${lock}-lock' + } + scope: resourceGroup +} + +module resourceGroup_roleAssignments '.bicep/nested_roleAssignments.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-RG-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + condition: contains(roleAssignment, 'condition') ? roleAssignment.condition : '' + delegatedManagedIdentityResourceId: contains(roleAssignment, 'delegatedManagedIdentityResourceId') ? roleAssignment.delegatedManagedIdentityResourceId : '' + } + scope: resourceGroup +}] + +@description('The name of the resource group.') +output name string = resourceGroup.name + +@description('The resource ID of the resource group.') +output resourceId string = resourceGroup.id + +@description('The location the resource was deployed into.') +output location string = resourceGroup.location diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/readme.md new file mode 100644 index 000000000..56da44049 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/readme.md @@ -0,0 +1,291 @@ +# Resource Groups `[Microsoft.Resources/resourceGroups]` + +This module deploys a resource group. + +## Navigation + +- [Resource types](#Resource-types) +- [Parameters](#Parameters) +- [Considerations](#Considerations) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## 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.Resources/resourceGroups` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2021-04-01/resourceGroups) | + +## Parameters + +**Required parameters** + +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Resource Group. | + +**Optional parameters** + +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | | Location of the Resource Group. It uses the deployment's location when not provided. | +| `lock` | string | `''` | `['', CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `managedBy` | string | `''` | | The ID of the resource that manages this resource group. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `tags` | object | `{object}` | | Tags of the storage account resource. | + + +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Considerations + +This module requires a User Assigned Identity (MSI, managed service identity) to exist, and this MSI has to have contributor rights on the subscription - that allows the Deployment Script to create the required Storage Account and the Azure Container Instance. + +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the resource group. | +| `resourceId` | string | The resource ID of the resource group. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other CARML 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 | +| :-- | :-- | +| `Microsoft.Authorization/locks/resourceGroup` | Local reference | + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Common

+ +
+ +via Bicep module + +```bicep +module resourceGroups './Microsoft.Resources/resourceGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-rrgcom' + params: { + // Required parameters + name: '<>rrgcom001' + // Non-required parameters + enableDefaultTelemetry: '' + lock: 'CanNotDelete' + roleAssignments: [ + { + principalIds: [ + '' + ] + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Reader' + } + ] + tags: { + Environment: 'Non-Prod' + 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": "<>rrgcom001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + }, + "lock": { + "value": "CanNotDelete" + }, + "roleAssignments": { + "value": [ + { + "principalIds": [ + "" + ], + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Reader" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +

Example 2: Min

+ +
+ +via Bicep module + +```bicep +module resourceGroups './Microsoft.Resources/resourceGroups/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-rrgmin' + params: { + // Required parameters + name: '<>rrgmin001' + // Non-required parameters + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

+ +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": "<>rrgmin001" + }, + // Non-required parameters + "enableDefaultTelemetry": { + "value": "" + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/version.json new file mode 100644 index 000000000..badc0a228 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/resourceGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/min/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/min/deploy.test.bicep new file mode 100644 index 000000000..843b48fab --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/min/deploy.test.bicep @@ -0,0 +1,22 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'rtmin' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/rg/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/rg/deploy.test.bicep new file mode 100644 index 000000000..f8d591710 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/rg/deploy.test.bicep @@ -0,0 +1,46 @@ +targetScope = 'subscription' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'ms.resources.tags-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param location 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 = 'rtrg' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: location +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-${serviceShort}' + params: { + enableDefaultTelemetry: enableDefaultTelemetry + onlyUpdate: false + resourceGroupName: resourceGroup.name + tags: { + Test: 'Yes' + TestToo: 'No' + } + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/sub/deploy.test.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/sub/deploy.test.bicep new file mode 100644 index 000000000..763274b45 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/.test/sub/deploy.test.bicep @@ -0,0 +1,27 @@ +targetScope = 'subscription' + +// ========== // +// 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 = 'rtsub' + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-${serviceShort}' + params: { + onlyUpdate: true + tags: { + Test: 'Yes' + TestToo: 'No' + } + enableDefaultTelemetry: enableDefaultTelemetry + } +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/deploy.bicep new file mode 100644 index 000000000..bfe7b0fd7 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/deploy.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +@description('Optional. Tags for the resource group. If not provided, removes existing tags.') +param tags object = {} + +@description('Optional. Instead of overwriting the existing tags, combine them with the new tags.') +param onlyUpdate bool = false + +@description('Optional. Name of the Resource Group to assign the tags to. If no Resource Group name is provided, and Subscription ID is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription.') +param resourceGroupName string = '' + +@description('Optional. Subscription ID of the subscription to assign the tags to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription.') +param subscriptionId string = subscription().id + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +var enableReferencedModulesTelemetry = false + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module tags_sub 'subscriptions/deploy.bicep' = if (!empty(subscriptionId) && empty(resourceGroupName)) { + name: '${deployment().name}-Tags-Sub' + params: { + onlyUpdate: onlyUpdate + tags: tags + location: location + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +module tags_rg 'resourceGroups/deploy.bicep' = if (!empty(resourceGroupName) && !empty(subscriptionId)) { + name: '${deployment().name}-Tags-RG' + scope: resourceGroup(resourceGroupName) + params: { + onlyUpdate: onlyUpdate + tags: tags + enableDefaultTelemetry: enableReferencedModulesTelemetry + } +} + +@description('The name of the tags resource.') +output name string = (!empty(resourceGroupName) && !empty(subscriptionId)) ? tags_rg.outputs.name : tags_sub.outputs.name + +@description('The applied tags.') +output tags object = (!empty(resourceGroupName) && !empty(subscriptionId)) ? tags_rg.outputs.tags : tags_sub.outputs.tags + +@description('The resource ID of the applied tags.') +output resourceId string = (!empty(resourceGroupName) && !empty(subscriptionId)) ? tags_rg.outputs.resourceId : tags_sub.outputs.resourceId diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/readme.md new file mode 100644 index 000000000..e66bde242 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/readme.md @@ -0,0 +1,234 @@ +# Resources Tags `[Microsoft.Resources/tags]` + +This module deploys Resources Tags on a subscription or resource group scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Resources/tags` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2021-04-01/tags) | + +## Parameters + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `onlyUpdate` | bool | `False` | Instead of overwriting the existing tags, combine them with the new tags. | +| `resourceGroupName` | string | `''` | Name of the Resource Group to assign the tags to. If no Resource Group name is provided, and Subscription ID is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription. | +| `subscriptionId` | string | `[subscription().id]` | Subscription ID of the subscription to assign the tags to. If no Resource Group name is provided, the module deploys at subscription level, therefore assigns the provided tags to the subscription. | +| `tags` | object | `{object}` | Tags for the resource group. If not provided, removes existing tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the tags resource. | +| `resourceId` | string | The resource ID of the applied tags. | +| `tags` | object | The applied tags. | + +## Cross-referenced modules + +_None_ + +## Deployment examples + +The following module usage examples are retrieved from the content of the files hosted in the module's `.test` folder. + >**Note**: The name of each example is based on the name of the file from which it is taken. + + >**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +

Example 1: Min

+ +
+ +via Bicep module + +```bicep +module tags './Microsoft.Resources/tags/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-rtmin' + params: { + enableDefaultTelemetry: '' + } +} +``` + +
+

+ +

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

+ +

Example 2: Rg

+ +
+ +via Bicep module + +```bicep +module tags './Microsoft.Resources/tags/deploy.bicep' = { + name: '${uniqueString(deployment().name, location)}-test-rtrg' + params: { + enableDefaultTelemetry: '' + onlyUpdate: false + resourceGroupName: '' + tags: { + Test: 'Yes' + TestToo: 'No' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "enableDefaultTelemetry": { + "value": "" + }, + "onlyUpdate": { + "value": false + }, + "resourceGroupName": { + "value": "" + }, + "tags": { + "value": { + "Test": "Yes", + "TestToo": "No" + } + } + } +} +``` + +
+

+ +

Example 3: Sub

+ +
+ +via Bicep module + +```bicep +module tags './Microsoft.Resources/tags/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-test-rtsub' + params: { + enableDefaultTelemetry: '' + onlyUpdate: true + tags: { + Test: 'Yes' + TestToo: 'No' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "enableDefaultTelemetry": { + "value": "" + }, + "onlyUpdate": { + "value": true + }, + "tags": { + "value": { + "Test": "Yes", + "TestToo": "No" + } + } + } +} +``` + +
+

diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep new file mode 100644 index 000000000..f189f85ba --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/.bicep/readTags.bicep @@ -0,0 +1,9 @@ +@description('Optional. The name of the tags resource.') +param name string = 'default' + +resource tags 'Microsoft.Resources/tags@2019-10-01' existing = { + name: name +} + +@description('Tags currently applied to the subscription level.') +output existingTags object = contains(tags.properties, 'tags') ? tags.properties.tags : {} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/deploy.bicep new file mode 100644 index 000000000..9bbea56fd --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/deploy.bicep @@ -0,0 +1,45 @@ +@description('Optional. Tags for the resource group. If not provided, removes existing tags.') +param tags object = {} + +@description('Optional. Instead of overwriting the existing tags, combine them with the new tags.') +param onlyUpdate bool = false + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module readTags '.bicep/readTags.bicep' = if (onlyUpdate) { + name: '${deployment().name}-ReadTags' +} + +var newTags = (onlyUpdate) ? union(readTags.outputs.existingTags, tags) : tags + +resource tag 'Microsoft.Resources/tags@2021-04-01' = { + name: 'default' + properties: { + tags: newTags + } +} + +@description('The name of the tags resource.') +output name string = tag.name + +@description('The resource ID of the applied tags.') +output resourceId string = tag.id + +@description('The name of the resource group the tags were applied to.') +output resourceGroupName string = resourceGroup().name + +@description('The applied tags.') +output tags object = newTags diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/readme.md new file mode 100644 index 000000000..1543ca864 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/readme.md @@ -0,0 +1,81 @@ +# Resources Tags ResourceGroups `[Microsoft.Resources/tags/resourceGroups]` + +This module deploys Resources Tags on a resource group scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Resources/tags` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2021-04-01/tags) | + +## Parameters + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `onlyUpdate` | bool | `False` | Instead of overwriting the existing tags, combine them with the new tags. | +| `tags` | object | `{object}` | Tags for the resource group. If not provided, removes existing tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the tags resource. | +| `resourceGroupName` | string | The name of the resource group the tags were applied to. | +| `resourceId` | string | The resource ID of the applied tags. | +| `tags` | object | The applied tags. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/resourceGroups/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep new file mode 100644 index 000000000..ab581cdea --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/.bicep/readTags.bicep @@ -0,0 +1,11 @@ +targetScope = 'subscription' + +@description('Optional. The name of the tags resource.') +param name string = 'default' + +resource tags 'Microsoft.Resources/tags@2021-04-01' existing = { + name: name +} + +@description('Tags currently applied to the subscription level.') +output existingTags object = contains(tags.properties, 'tags') ? tags.properties.tags : {} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/deploy.bicep b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/deploy.bicep new file mode 100644 index 000000000..1fc1d58b1 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/deploy.bicep @@ -0,0 +1,48 @@ +targetScope = 'subscription' + +@description('Optional. Tags for the resource group. If not provided, removes existing tags.') +param tags object = {} + +@description('Optional. Instead of overwriting the existing tags, combine them with the new tags.') +param onlyUpdate bool = false + +@sys.description('Optional. Location deployment metadata.') +param location string = deployment().location + +@description('Optional. Enable telemetry via a Globally Unique Identifier (GUID).') +param enableDefaultTelemetry bool = true + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name, location)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +module readTags '.bicep/readTags.bicep' = if (onlyUpdate) { + name: '${deployment().name}-ReadTags' +} + +var newTags = (onlyUpdate) ? union(readTags.outputs.existingTags, tags) : tags + +resource tag 'Microsoft.Resources/tags@2021-04-01' = { + name: 'default' + properties: { + tags: newTags + } +} + +@description('The name of the tags resource.') +output name string = tag.name + +@description('The applied tags.') +output tags object = newTags + +@description('The resource ID of the applied tags.') +output resourceId string = tag.id diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/readme.md b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/readme.md new file mode 100644 index 000000000..19ce0dbc8 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/readme.md @@ -0,0 +1,81 @@ +# Resources Tags Subscriptions `[Microsoft.Resources/tags/subscriptions]` + +This module deploys Resources Tags on a subscription scope. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Resources/tags` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2021-04-01/tags) | + +## Parameters + +**Optional parameters** + +| Parameter Name | Type | Default Value | Description | +| :-- | :-- | :-- | :-- | +| `enableDefaultTelemetry` | bool | `True` | Enable telemetry via a Globally Unique Identifier (GUID). | +| `location` | string | `[deployment().location]` | Location deployment metadata. | +| `onlyUpdate` | bool | `False` | Instead of overwriting the existing tags, combine them with the new tags. | +| `tags` | object | `{object}` | Tags for the resource group. If not provided, removes existing tags. | + + +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the tags resource. | +| `resourceId` | string | The resource ID of the applied tags. | +| `tags` | object | The applied tags. | + +## Cross-referenced modules + +_None_ diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/subscriptions/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/version.json b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/version.json new file mode 100644 index 000000000..41f66cc99 --- /dev/null +++ b/patterns/avd/templates/carml/1.3.0/Microsoft.Resources/tags/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.1" +} diff --git a/patterns/avd/templates/carml/carmlVersionTracking.md b/patterns/avd/templates/carml/carmlVersionTracking.md new file mode 100644 index 000000000..93c73f831 --- /dev/null +++ b/patterns/avd/templates/carml/carmlVersionTracking.md @@ -0,0 +1,14 @@ +# CARML +# Module version tracking + +Mapping of AVD accelerator code and CARML module versions + +## Versions mapping + +CARML version | AVD accelerator version | Clone date | Delta | Tested +---|---|---|---|--- +0.4.0 | 1.0.0 | 03/03/2022 | - | yes +0.4.0 | 1.0.1 | 04/20/2022 | 120 commits ahead of 1.0.0 | no +0.5.0 | 1.2.0 | 05/13/2022 | - | yes +0.5.0 | 1.2.1 | 08/31/2022 | - | no +0.8.0 | 1.3.0 | 04/07/2023 | - | yes diff --git a/patterns/avd/templates/deploy.bicep b/patterns/avd/templates/deploy.bicep new file mode 100644 index 000000000..6b8454d5a --- /dev/null +++ b/patterns/avd/templates/deploy.bicep @@ -0,0 +1,2283 @@ +targetScope = 'subscription' + +/* +@description('Determine if you would like to enable all the alerts after deployment.') +param SetEnabled bool = false + */ + +@description('Location of needed scripts to deploy solution.') +param _ArtifactsLocation string = 'https://raw.githubusercontent.com/Azure/avdaccelerator/main/workload/scripts/alerts/' + +@description('SaS token if needed for script location.') +@secure() +param _ArtifactsLocationSasToken string = '' + +@description('Alert Name Prefix (Dash will be added after prefix for you.)') +param AlertNamePrefix string = 'AVD' + +@description('Flag to determine if AVD VMs and AVD resources are all in the same Resource Group.') +param AllResourcesSameRG bool = true + +@description('Determine if you would like to set all deployed alerts to auto-resolve.') +param AutoResolveAlert bool = true + +@description('The Distribution Group that will receive email alerts for AVD.') +param DistributionGroup string + +@allowed([ + 'd' + 'p' + 't' +]) +@description('The environment is which these resources will be deployed, i.e. Test, Production, Development.') +param Environment string = 't' + +@description('Array of objects with the Resource ID for colHostPoolName and colVMresGroup for each Host Pool.') +param HostPoolInfo array = [] + +@description('Host Pool Resource IDs (array)') +param HostPools array = [] + +@description('Azure Region for Resources.') +param Location string = deployment().location + +@description('The Resource ID for the Log Analytics Workspace.') +param LogAnalyticsWorkspaceResourceId string + +@description('Resource Group to deploy the Alerts Solution in.') +param ResourceGroupName string + +//placeholder needed for template validation when VMs in separate RG selected - desktop reader deployment fails otherwise +@description('AVD Resource Group ID with ALL resources including VMs') +param AVDResourceGroupId string = '/subscriptions//resourceGroups/' + +@description('Flag to determine if a new resource group needs to be created for Alert resources.') +@allowed([ + 'New' + 'Existing' +]) +param ResourceGroupStatus string + +@description('The Resource IDs for the Azure Files Storage Accounts used for FSLogix profile storage.') +param StorageAccountResourceIds array = [] + +@description('ISO 8601 timestamp used for the deployment names and the Automation runbook schedule.') +param time string = utcNow() + +@description('The Resource IDs for the Azure NetApp Volumes used for FSLogix profile storage.') +param ANFVolumeResourceIds array = [] + +param Tags object = {} + +var ActionGroupName = 'ag-avdmetrics-${Environment}-${Location}' +var AlertDescriptionHeader = 'Automated AVD Alert Deployment Solution (v2.1.5)\n' // DESCRIPTION HEADER AND VERSION <----------------------------- +var AutomationAccountName = 'aa-avdmetrics-${Environment}-${Location}-${AlertNamePrefix}' +var CloudEnvironment = environment().name +var ResourceGroupCreate = ResourceGroupStatus == 'New' ? true : false +var RunbookNameGetStorage = 'AvdStorageLogData' +var RunbookNameGetHostPool = 'AvdHostPoolLogData' +var RunbookScriptGetStorage = 'Get-StorAcctInfo.ps1${_ArtifactsLocationSasToken}' +var RunbookScriptGetHostPool = 'Get-HostPoolInfo.ps1${_ArtifactsLocationSasToken}' +var StorAcctRGsAll = [for item in StorageAccountResourceIds: split(item, '/')[4]] +var StorAcctRGs = union(StorAcctRGsAll, []) +// var UsrManagedIdentityName = 'id-ds-avdAlerts-Deployment' + +var RoleAssignments = { + DesktopVirtualizationRead: { + Name: 'Desktop-Virtualization-Reader' + GUID: '49a72310-ab8d-41df-bbb0-79b649203868' + } + StoreAcctContrib: { + Name: 'Storage-Account-Contributor' + GUID: '17d1049b-9a84-46fb-8f53-869881c3d3ab' + } + LogAnalyticsContributor: { + Name: 'LogAnalytics-Contributor' + GUID: '92aaf0da-9dab-42b6-94a3-d43ce8d16293' + } +} +// '49a72310-ab8d-41df-bbb0-79b649203868' // Desktop Virtualization Reader +// '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor +// '92aaf0da-9dab-42b6-94a3-d43ce8d16293' // Log Analtyics Contributor - allows writing to workspace for Host Pool and Storage Logic Apps + +var LogAlertsHostPool = [ + {// Based on Runbook script Output to LAW + name: '${AlertNamePrefix}-HP-Cap-85Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-Capacity 85 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution for xHostPoolNamex.\n-->Last Number in the string is the Percentage Remaining for the Host Pool\nOutput is:\nHostPoolName|ResourceGroup|Type|MaxSessionLimit|NumberHosts|TotalUsers|DisconnectedUser|ActiveUsers|SessionsAvailable|HostPoolPercentageLoad' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT30M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + AzureDiagnostics + | where Category has "JobStreams" and StreamType_s == "Output" and RunbookName_s == "AvdHostPoolLogData" + | sort by TimeGenerated + | where TimeGenerated > now() - 5m + | extend HostPoolName=tostring(split(ResultDescription, '|')[0]) + | extend ResourceGroup=tostring(split(ResultDescription, '|')[1]) + | extend Type=tostring(split(ResultDescription, '|')[2]) + | extend MaxSessionLimit=toint(split(ResultDescription, '|')[3]) + | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4]) + | extend UserSessionsTotal=toint(split(ResultDescription, '|')[5]) + | extend UserSessionsDisconnected=toint(split(ResultDescription, '|')[6]) + | extend UserSessionsActive=toint(split(ResultDescription, '|')[7]) + | extend UserSessionsAvailable=toint(split(ResultDescription, '|')[8]) + | extend HostPoolPercentLoad=toint(split(ResultDescription, '|')[9]) + | extend HPResourceId=tostring(split(ResultDescription, '|')[13]) + | extend ResourceId=tostring(HPResourceId) + | where HostPoolPercentLoad >= 85 and HostPoolPercentLoad < 95 + | where HostPoolName =~ 'xHostPoolNamex' + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'HostPoolName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsTotal' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsDisconnected' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsActive' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsAvailable' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPoolPercentLoad' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: 'ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + {// Based on Runbook script Output to LAW + name: '${AlertNamePrefix}-HP-Cap-50Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-Capacity 50 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution for xHostPoolNamex.\n-->Last Number in the string is the Percentage Remaining for the Host Pool\nOutput is:\nHostPoolName|ResourceGroup|Type|MaxSessionLimit|NumberHosts|TotalUsers|DisconnectedUser|ActiveUsers|SessionsAvailable|HostPoolPercentageLoad' + severity: 3 + evaluationFrequency: 'PT5M' + windowSize: 'PT30M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + AzureDiagnostics + | where Category has "JobStreams" and StreamType_s == "Output" and RunbookName_s == "AvdHostPoolLogData" + | sort by TimeGenerated + | where TimeGenerated > now() - 5m + | extend HostPoolName=tostring(split(ResultDescription, '|')[0]) + | extend ResourceGroup=tostring(split(ResultDescription, '|')[1]) + | extend Type=tostring(split(ResultDescription, '|')[2]) + | extend MaxSessionLimit=toint(split(ResultDescription, '|')[3]) + | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4]) + | extend UserSessionsTotal=toint(split(ResultDescription, '|')[5]) + | extend UserSessionsDisconnected=toint(split(ResultDescription, '|')[6]) + | extend UserSessionsActive=toint(split(ResultDescription, '|')[7]) + | extend UserSessionsAvailable=toint(split(ResultDescription, '|')[8]) + | extend HostPoolPercentLoad=toint(split(ResultDescription, '|')[9]) + | extend HPResourceId=tostring(split(ResultDescription, '|')[13]) + | extend ResourceId=tostring(HPResourceId) + | where HostPoolPercentLoad >= 50 and HostPoolPercentLoad < 85 + | where HostPoolName =~ 'xHostPoolNamex' + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'HostPoolName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsTotal' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsDisconnected' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsActive' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsAvailable' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPoolPercentLoad' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: 'ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + {// Based on Runbook script Output to LAW + name: '${AlertNamePrefix}-HP-Cap-95Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-Capacity 95 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution for xHostPoolNamex.\n-->Last Number in the string is the Percentage Remaining for the Host Pool\nOutput is:\nHostPoolName|ResourceGroup|Type|MaxSessionLimit|NumberHosts|TotalUsers|DisconnectedUser|ActiveUsers|SessionsAvailable|HostPoolPercentageLoad' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT30M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + AzureDiagnostics + | where Category has "JobStreams" and StreamType_s == "Output" and RunbookName_s == "AvdHostPoolLogData" + | sort by TimeGenerated + | where TimeGenerated > now() - 5m + | extend HostPoolName=tostring(split(ResultDescription, '|')[0]) + | extend ResourceGroup=tostring(split(ResultDescription, '|')[1]) + | extend Type=tostring(split(ResultDescription, '|')[2]) + | extend MaxSessionLimit=toint(split(ResultDescription, '|')[3]) + | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4]) + | extend UserSessionsTotal=toint(split(ResultDescription, '|')[5]) + | extend UserSessionsDisconnected=toint(split(ResultDescription, '|')[6]) + | extend UserSessionsActive=toint(split(ResultDescription, '|')[7]) + | extend UserSessionsAvailable=toint(split(ResultDescription, '|')[8]) + | extend HostPoolPercentLoad=toint(split(ResultDescription, '|')[9]) + | extend HPResourceId=tostring(split(ResultDescription, '|')[13]) + | extend ResourceId=tostring(HPResourceId) + | where HostPoolPercentLoad >= 95 + | where HostPoolName =~ 'xHostPoolNamex' + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'HostPoolName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsTotal' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsDisconnected' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsActive' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserSessionsAvailable' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPoolPercentLoad' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: 'ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-NoResAvail-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-No Resources Available (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Catastrophic Event! Indicates potential problems with dependencies, diagnose and resolve for xHostPoolNamex.' + severity: 1 + evaluationFrequency: 'PT15M' + windowSize: 'PT15M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: 'WVDConnections \n| where TimeGenerated > ago (15m) \n| where _ResourceId contains "xHostPoolNamex" \n| project-away TenantId,SourceSystem \n| summarize arg_max(TimeGenerated, *), StartTime = min(iff(State== \'Started\', TimeGenerated , datetime(null) )), ConnectTime = min(iff(State== \'Connected\', TimeGenerated , datetime(null) )) by CorrelationId \n| join kind=leftouter (WVDErrors\n |summarize Errors=makelist(pack(\'Code\', Code, \'CodeSymbolic\', CodeSymbolic, \'Time\', TimeGenerated, \'Message\', Message ,\'ServiceError\', ServiceError, \'Source\', Source)) by CorrelationId \n ) on CorrelationId\n| join kind=leftouter (WVDCheckpoints\n | summarize Checkpoints=makelist(pack(\'Time\', TimeGenerated, \'Name\', Name, \'Parameters\', Parameters, \'Source\', Source)) by CorrelationId \n | mv-apply Checkpoints on ( \n order by todatetime(Checkpoints[\'Time\']) asc\n | summarize Checkpoints=makelist(Checkpoints)\n )\n ) on CorrelationId \n| project-away CorrelationId1, CorrelationId2 \n| order by TimeGenerated desc\n| where Errors[0].CodeSymbolic == "ConnectionFailedNoHealthyRdshAvailable"\n\n' + timeAggregation: 'Count' + dimensions: [ + { + name: 'UserName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'SessionHostName' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-DiscUser24Hrs-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-Disconnected User over 24 Hours (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Verify Remote Desktop Policies are applied relating to Session Limits for xHostPoolNamex. This could impact your scaling plan as well.' + severity: 2 + evaluationFrequency: 'PT1H' + windowSize: 'PT1H' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: '// Session duration \n// Lists users by session duration in the last 24 hours. \n// The "State" provides information on the connection stage of an activity.\n// The delta between "Connected" and "Completed" provides the connection time for a specific connection.\nWVDConnections \n| where TimeGenerated > ago(24h) \n| where State == "Connected" \n| where _ResourceId contains "xHostPoolNamex" \n| project CorrelationId , UserName, ConnectionType, StartTime=TimeGenerated, SessionHostName\n| join (WVDConnections \n | where State == "Completed" \n | project EndTime=TimeGenerated, CorrelationId) \n on CorrelationId \n| project Duration = EndTime - StartTime, ConnectionType, UserName, SessionHostName\n| where Duration >= timespan(24:00:00)\n| sort by Duration desc' + timeAggregation: 'Count' + dimensions: [ + { + name: 'UserName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'SessionHostName' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-DiscUser72Hrs-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-Disconnected User over 72 Hours (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Verify Remote Desktop Policies are applied relating to Session Limits for xHostPoolNamex. This could impact your scaling plan as well.' + severity: 1 + evaluationFrequency: 'PT1H' + windowSize: 'PT1H' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: '// Session duration \n// Lists users by session duration in the last 24 hours. \n// The "State" provides information on the connection stage of an activity.\n// The delta between "Connected" and "Completed" provides the connection time for a specific connection.\nWVDConnections \n| where TimeGenerated > ago(24h) \n| where State == "Connected" \n| where _ResourceId contains "xHostPoolNamex" \n| project CorrelationId , UserName, ConnectionType, StartTime=TimeGenerated, SessionHostName\n| join (WVDConnections \n | where State == "Completed" \n | project EndTime=TimeGenerated, CorrelationId) \n on CorrelationId \n| project Duration = EndTime - StartTime, ConnectionType, UserName, SessionHostName\n| where Duration >= timespan(72:00:00)\n| sort by Duration desc' + timeAggregation: 'Count' + dimensions: [ + { + name: 'UserName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'SessionHostName' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-LocDskFree10Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-Local Disk Free Space 10 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Disk space Moderately Low. \nConsider review of the VM local C drive and determine what is consuming disk space for the VM in xHostPoolNamex. This could be local profiles or temp files that need to be cleaned up or removed.' + severity: 2 + evaluationFrequency: 'PT15M' + windowSize: 'PT15M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + Perf + | where TimeGenerated > ago(15m) + | where ObjectName == "LogicalDisk" and CounterName == "% Free Space" + | where InstanceName !contains "D:" + | where InstanceName !contains "_Total"| where CounterValue <= 10.00 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, CounterValue, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where TimeGenerated > ago(15m) + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool, _ResourceId + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-LocDskFree5Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-Local Disk Free Space 5 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Disk space Critically Low. \nConsider review of the VM local C drive and determine what is consuming disk space for the VM in xHostPoolNamex. This could be local profiles or temp files that need to be cleaned up or removed.' + severity: 1 + evaluationFrequency: 'PT15M' + windowSize: 'PT15M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + Perf + | where TimeGenerated > ago(15m) + | where ObjectName == "LogicalDisk" and CounterName == "% Free Space" + | where InstanceName !contains "D:" + | where InstanceName !contains "_Total"| where CounterValue <= 5.00 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, CounterValue, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where TimeGenerated > ago(15m) + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool, _ResourceId + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf5PrcntFree-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Less Than 5 Percent Free Space (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profiles Service logged Event ID 33. Expand User\'s Virtual Profile Disk and/or clean up user profile data on the VM in xHostPoolNamex.' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT5M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Admin" + | where EventLevelName == "Warning" + | where EventID == 34 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf2PrcntFree-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Less Than 2 Percent Free Space (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profiles Service logged Event ID 34. Expand User\'s Virtual Profile Disk and/or clean up user profile data on the VM in xHostPoolNamex.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT5M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Admin" + | where EventLevelName == "Error" + | where EventID == 33 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf-NetwrkIssue-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Failed due to Network Issue (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profiles Service logged Event ID 43. Verify network communications between the storage and AVD VM related to xHostPoolNamex.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'P1D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Admin" + | where EventLevelName == "Error" + | where EventID == 43 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf-FailAttVHD-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Disk Failed to Attach (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profiles Service logged an Event ID 52 or 40. Investigate error details for reason regarding xHostPoolNamex.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'P1D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Admin" + | where EventLevelName == "Error" + | where EventID == 52 or EventID == 40 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf-SvcDisabled-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Service Disabled (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profile Service Disabled. Determine why service was disabled and re-enable / start the FSLogix service. Regarding xHostPoolNamex' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'P1D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Admin" + | where EventLevelName == "Warning" + | where EventID == 60 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf-DskCmpFail-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Disk Compaction Failed (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profile Service logged Event ID 62 or 63. The profile Disk was marked for compaction due to additional white space but failed. See error details for additional information regarding xHostPoolNamex.' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'P1D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Admin" + | where EventLevelName == "Error" + | where EventID == 62 or EventID == 63 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-FSLgxProf-DskInUse-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-FSLogix Profile Disk Attached to another VM (xHostPoolNamex)' + description: '${AlertDescriptionHeader}User Profile Service logged an Event ID 51. This indicates that a user attempted to load their profile disk but it was in use or possibly mapped to another VM. Ensure the user is not connected to another host pool or remote app with the same profile. Regarding xHostPoolNamex.' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'P1D' + criteria: { + allOf: [ + { + query: ''' + Event + | where EventLog == "Microsoft-FSLogix-Apps/Operational" + | where EventLevelName == "Warning" + | where EventID == 51 + | parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" ResourceGroup "/providers/microsoft.compute/virtualmachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | project ComputerName, RenderedDescription, subscription, ResourceGroup, TimeGenerated + | join kind = leftouter + ( + WVDAgentHealthStatus + | where _ResourceId contains "xHostPoolNamex" + | parse _ResourceId with "/subscriptions/" subscriptionAgentHealth "/resourcegroups/" ResourceGroupAgentHealth "/providers/microsoft.desktopvirtualization/hostpools/" HostPool + | parse SessionHostResourceId with "/subscriptions/" VMsubscription "/resourceGroups/" VMresourceGroup "/providers/Microsoft.Compute/virtualMachines/" ComputerName + | extend ComputerName=tolower(ComputerName) + | summarize arg_max(TimeGenerated,*) by ComputerName + | project VMresourceGroup, ComputerName, HostPool + ) on ComputerName + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'ComputerName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'RenderedDescription' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'VMresourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: '_ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-HlthChkFailure-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-Health Check Failure (xHostPoolNamex)' + description: '${AlertDescriptionHeader}VM is available for use but one of the dependent resources is in a failed state for hostpool xHostPoolNamex' + severity: 1 + evaluationFrequency: 'PT15M' + windowSize: 'PT15M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: '// HealthChecks of SessionHost \n// Renders a summary of SessionHost health status. \nlet MapToDesc = (idx: long) {\n case(idx == 0, "DomainJoin",\n idx == 1, "DomainTrust",\n idx == 2, "FSLogix",\n idx == 3, "SxSStack",\n idx == 4, "URLCheck",\n idx == 5, "GenevaAgent",\n idx == 6, "DomainReachable",\n idx == 7, "WebRTCRedirector",\n idx == 8, "SxSStackEncryption",\n idx == 9, "IMDSReachable",\n idx == 10, "MSIXPackageStaging",\n "InvalidIndex")\n};\nWVDAgentHealthStatus\n| where TimeGenerated > ago(10m)\n| where Status != \'Available\'\n| where AllowNewSessions = True\n| extend CheckFailed = parse_json(SessionHostHealthCheckResult)\n| mv-expand CheckFailed\n| where CheckFailed.AdditionalFailureDetails.ErrorCode != 0\n| extend HealthCheckName = tolong(CheckFailed.HealthCheckName)\n| extend HealthCheckResult = tolong(CheckFailed.HealthCheckResult)\n| extend HealthCheckDesc = MapToDesc(HealthCheckName)\n| where HealthCheckDesc != \'InvalidIndex\'\n| where _ResourceId contains "xHostPoolNamex"\n| parse _ResourceId with "/subscriptions/" subscription "/resourcegroups/" HostPoolResourceGroup "/providers/microsoft.desktopvirtualization/hostpools/" HostPool\n| parse SessionHostResourceId with "/subscriptions/" HostSubscription "/resourceGroups/" SessionHostRG " /providers/Microsoft.Compute/virtualMachines/" SessionHostName\n' + timeAggregation: 'Count' + dimensions: [ + { + name: 'SessionHostName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HealthCheckDesc' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'SessionHostRG' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-VM-PersnlAssigndUnhlthy-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-Personal Assigned Health Check Failure (xHostPoolNamex)' + description: '${AlertDescriptionHeader}VM is assigned to a user but one of the dependent resources is in a failed state for hostpool xHostPoolNamex' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT5M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + // Personal Session Host where Health status is NOT healthy and the VM is assigned + AzureDiagnostics + | where Category has "JobStreams" + and StreamType_s == "Output" + and RunbookName_s == "AvdHostPoolLogData" + | sort by TimeGenerated + | where TimeGenerated > ago(15m) + | extend HostPoolName=tostring(split(ResultDescription, '|')[0]) + | extend ResourceGroup=tostring(split(ResultDescription, '|')[1]) + | extend Type=tostring(split(ResultDescription, '|')[2]) + | extend NumberSessionHosts=toint(split(ResultDescription, '|')[4]) + | extend UserSessionsActive=toint(split(ResultDescription, '|')[7]) + | extend NumPersonalUnhealthy=toint(split(ResultDescription, '|')[10]) + | extend PersonalSessionHost=extract_json("$.SessionHost", tostring(split(ResultDescription, '|')[11]), typeof(string)) + | extend PersonalAssignedUser=extract_json("$.AssignedUser", tostring(split(ResultDescription, '|')[11]), typeof(string)) + | where HostPoolName =~ 'xHostPoolNamex' + | where Type == 'Personal' + | where NumPersonalUnhealthy > 0 + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'HostPoolName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'PersonalSessionHost' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'PersonalAssignedUser' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + { + name: '${AlertNamePrefix}-HP-Usr-ConnctnFailed-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-User-Connection Failed (xHostPoolNamex)' + description: '${AlertDescriptionHeader}While trying to connect to xHostPoolNamex a user had an error and failed to connect to a VM. There are lots of variables between the end uers and AVD VMs. If this is frequent for the user, determine if their Internet connection is slow or latency is over 150 ms. Regarding xHostPoolNamex.' + severity: 3 + evaluationFrequency: 'PT15M' + windowSize: 'PT15M' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + // Connection Errors + // List connection checkpoints and errors for each connection attempt, along with detailed information across all users. + //You can also uncomment the where clause to filter to a specific user if you are troubleshooting an issue. + WVDConnections + //| where UserName == "upn.here@contoso.com" + | project-away TenantId,SourceSystem + | summarize arg_max(TimeGenerated, *), StartTime = min(iff(State=='Started', TimeGenerated , datetime(null) )), ConnectTime = min(iff(State=='Connected', TimeGenerated , datetime(null) )) by CorrelationId + | join kind=leftouter + ( + WVDErrors + |summarize Errors=make_list(pack('Code', Code, 'CodeSymbolic', CodeSymbolic, 'Time', TimeGenerated, 'Message', Message ,'ServiceError', ServiceError, 'Source', Source)) by CorrelationId + ) on CorrelationId + | join kind=leftouter + ( + WVDCheckpoints + | summarize Checkpoints=make_list(pack('Time', TimeGenerated, 'Name', Name, 'Parameters', Parameters, 'Source', Source)) by CorrelationId + | mv-apply Checkpoints on + ( + order by todatetime(Checkpoints['Time']) asc + | summarize Checkpoints=make_list(Checkpoints) + ) + ) on CorrelationId + | project-away CorrelationId1, CorrelationId2 + | order by TimeGenerated desc + | where TimeGenerated > ago(15m) + | extend ResourceGroup=tostring(split(_ResourceId, '/')[4]) + | extend HostPool=tostring(split(_ResourceId, '/')[8]) + | where HostPool =~ 'xHostPoolNamex' + | extend ErrorShort=tostring(Errors[0].CodeSymbolic) + | extend ErrorMessage=tostring(Errors[0].Message) + | project TimeGenerated, HostPool, ResourceGroup, UserName, ClientOS, ClientVersion, ClientSideIPAddress, ConnectionType, ErrorShort, ErrorMessage + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'HostPool' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ResourceGroup' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'UserName' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ClientOS' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ClientVersion' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ClientSideIPAddress' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ConnectionType' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ErrorShort' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'ErrorMessage' + operator: 'Include' + values: [ + '*' + ] + } + ] + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } +] + +var LogAlertsStorage = [ + {// Based on Runbook script Output to LAW + name: '${AlertNamePrefix}-StorLowSpaceAzFile-15PrcntRem' + displayName: '${AlertNamePrefix}-Storage-Low Space on Azure File Share-15 Percent Remaining' + description: '${AlertDescriptionHeader}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution.\nNOTE: The Runbook will FAIL if Networking for the storage account has anything other than "Enabled from all networks"\n-->Last Number in the string is the Percentage Remaining for the Share.\nOutput: ResultsDescription\nStorageType,Subscription,ResourceGroup,StorageAccount,ShareName,Quota,GBUsed,PercentRemaining' + severity: 2 + evaluationFrequency: 'PT10M' + windowSize: 'PT1H' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + AzureDiagnostics + | where Category has "JobStreams" and StreamType_s == "Output" and RunbookName_s == "AvdStorageLogData" + | where split(ResultDescription, ',')[1] <> "" + // StorageType / Subscription / RG / StorAcct / Share / Quota / GB Used / %Available + | extend StorageType=split(ResultDescription, ',')[0] + | extend Subscription=split(ResultDescription, ',')[1] + | extend ResourceGroup=split(ResultDescription, ',')[2] + | extend StorageAccount=tostring(split(ResultDescription, ',')[3]) + | extend Share=tostring(split(ResultDescription, ',')[4]) + | extend GBShareQuota=split(ResultDescription, ',')[5] + | extend GBUsed=split(ResultDescription, ',')[6] + | extend PercentAvailable=round(toreal(split(ResultDescription, ',')[7])) + | extend ResourceId=tostring(split(ResultDescription, ',')[8]) + | summarize arg_max(TimeGenerated, *) by Share + | where PercentAvailable <= 15.00 + | project TimeGenerated,ResourceId, StorageAccount, Share, PercentAvailable + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'StorageAccount' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'Share' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: 'ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } + {// Based on Runbook script Output to LAW + name: '${AlertNamePrefix}-StorLowSpaceAzFile-5PrcntRem' + displayName: '${AlertNamePrefix}-Storage-Low Space on Azure File Share-5 Percent Remaining' + description: '${AlertDescriptionHeader}This alert is based on the Action Account and Runbook that populates the Log Analytics specificed with the AVD Metrics Deployment Solution.\nNOTE: The Runbook will FAIL if Networking for the storage account has anything other than "Enabled from all networks"\n-->Last Number in the string is the Percentage Remaining for the Share.\nOutput: ResultsDescription\nStorageType,Subscription,ResourceGroup,StorageAccount,ShareName,Quota,GBUsed,PercentRemaining' + severity: 1 + evaluationFrequency: 'PT10M' + windowSize: 'PT1H' + overrideQueryTimeRange: 'P2D' + criteria: { + allOf: [ + { + query: ''' + AzureDiagnostics + | where Category has "JobStreams" and StreamType_s == "Output" and RunbookName_s == "AvdStorageLogData" + | where split(ResultDescription, ',')[1] <> "" + // StorageType / Subscription / RG / StorAcct / Share / Quota / GB Used / %Available + | extend StorageType=split(ResultDescription, ',')[0] + | extend Subscription=split(ResultDescription, ',')[1] + | extend ResourceGroup=split(ResultDescription, ',')[2] + | extend StorageAccount=tostring(split(ResultDescription, ',')[3]) + | extend Share=tostring(split(ResultDescription, ',')[4]) + | extend GBShareQuota=split(ResultDescription, ',')[5] + | extend GBUsed=split(ResultDescription, ',')[6] + | extend PercentAvailable=round(toreal(split(ResultDescription, ',')[7])) + | extend ResourceId=tostring(split(ResultDescription, ',')[8]) + | summarize arg_max(TimeGenerated, *) by Share + | where PercentAvailable <= 5.00 + | project TimeGenerated,ResourceId, StorageAccount, Share, PercentAvailable + ''' + timeAggregation: 'Count' + dimensions: [ + { + name: 'StorageAccount' + operator: 'Include' + values: [ + '*' + ] + } + { + name: 'Share' + operator: 'Include' + values: [ + '*' + ] + } + ] + resourceIdColumn: 'ResourceId' + operator: 'GreaterThanOrEqual' + threshold: 1 + failingPeriods: { + numberOfEvaluationPeriods: 1 + minFailingPeriodsToAlert: 1 + } + } + ] + } + } +] +var MetricAlerts = { + storageAccounts: [ + { + name: '${AlertNamePrefix}-StorAcct-Ovr-50msLatncy' + displayName: '${AlertNamePrefix}-Storage-Over 50ms Latency for Storage Acct' + description: '${AlertDescriptionHeader}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis alert is specific to the Storage Account itself and does not include network latency.\nFor additional details on troubleshooting see:\n"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests"' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 50 + name: 'Metric1' + metricName: 'SuccessServerLatency' + operator: 'GreaterThan' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.Storage/storageAccounts' + } + { + name: '${AlertNamePrefix}-StorAcct-Ovr-100msLatncy' + displayName: '${AlertNamePrefix}-Storage-Over 100ms Latency for Storage Acct' + description: '${AlertDescriptionHeader}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis alert is specific to the Storage Account itself and does not include network latency.\nFor additional details on troubleshooting see:\n"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests"' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 100 + name: 'Metric1' + metricName: 'SuccessServerLatency' + operator: 'GreaterThan' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.Storage/storageAccounts' + } + { + name: '${AlertNamePrefix}-StorAcct-Ovr-50msLatncyClnt-Stor' + displayName: '${AlertNamePrefix}-Storage-Over 50ms Latency Between Client-Storage' + description: '${AlertDescriptionHeader}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis is a total latency from end to end between the Host VM and Storage to include network.\nFor additional details on troubleshooting see:\n"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests"' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 50 + name: 'Metric1' + metricName: 'SuccessE2ELatency' + operator: 'GreaterThan' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.Storage/storageAccounts' + } + { + name: '${AlertNamePrefix}-StorAcct-Ovr-100msLatncyClnt-Stor' + displayName: '${AlertNamePrefix}-Storage-Over 100ms Latency Between Client-Storage' + description: '${AlertDescriptionHeader}\nThis could indicate a lag or poor performance for user Profiles or Apps using MSIX App Attach.\nThis is a total latency from end to end between the Host VM and Storage to include network.\nFor additional details on troubleshooting see:\n"https://learn.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#very-high-latency-for-requests"' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 100 + name: 'Metric1' + metricName: 'SuccessE2ELatency' + operator: 'GreaterThan' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.Storage/storageAccounts' + } + { + name: '${AlertNamePrefix}-StorAzFilesAvailBlw-99-Prcnt' + displayName: '${AlertNamePrefix}-Storage-Azure Files Availability' + description: '${AlertDescriptionHeader}\nThis could indicate storage is unavailable for user Profiles or Apps using MSIX App Attach.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT5M' + criteria: { + allOf: [ + { + threshold: 99 + name: 'Metric1' + metricName: 'Availability' + operator: 'LessThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.Storage/storageAccounts' + } + ] + fileShares: [ + { + name: '${AlertNamePrefix}-StorPossThrottlingHighIOPs' + displayName: '${AlertNamePrefix}-Storage-Possible Throttling Due to High IOPs' + description: '${AlertDescriptionHeader}\nThis indicates you may be maxing out the allowed IOPs.\nhttps://docs.microsoft.com/en-us/azure/storage/files/storage-troubleshooting-files-performance#how-to-create-an-alert-if-a-file-share-is-throttled' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 1 + name: 'Metric1' + metricName: 'Transactions' + dimensions: [ + { + name: 'ResponseType' + operator: 'Include' + values: [ + 'SuccessWithThrottling' + 'SuccessWithShareIopsThrottling' + 'ClientShareIopsThrottlingError' + ] + } + { + name: 'FileShare' + operator: 'Include' + values: [ + 'SuccessWithShareEgressThrottling' + 'SuccessWithShareIngressThrottling' + 'SuccessWithShareIopsThrottling' + 'ClientShareEgressThrottlingError' + 'ClientShareIngressThrottlingError' + 'ClientShareIopsThrottlingError' + ] + } + ] + operator: 'GreaterThanOrEqual' + timeAggregation: 'Total' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.Storage/storageAccounts/fileServices' + } + ] + anf: [ + { + name: '${AlertNamePrefix}-StorLowSpcANF-15-PrcntRem' + displayName: '${AlertNamePrefix}-Storage-Low Space on ANF Share-15 Percent Remaining' + description: '${AlertDescriptionHeader}Storage for the follow Azure NetApp volume is Moderately low. Verify sufficient storage is available and expand when/where needed.' + severity: 2 + evaluationFrequency: 'PT1H' + windowSize: 'PT1H' + criteria: { + allOf: [ + { + threshold: 85 + name: 'Metric1' + metricNamespace: 'microsoft.netapp/netappaccounts/capacitypools/volumes' + metricName: 'VolumeConsumedSizePercentage' + operator: 'GreaterThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes' + } + { + name: '${AlertNamePrefix}-StorLowSpcANF-5-PrcntRem' + displayName: '${AlertNamePrefix}-Storage-Low Space on ANF Share-5 Percent Remaining' + description: '${AlertDescriptionHeader}Storage for the follow Azure NetApp volume is Critically low. Verify sufficient storage is available and expand when/where needed.' + severity: 1 + evaluationFrequency: 'PT1H' + windowSize: 'PT1H' + criteria: { + allOf: [ + { + threshold: 95 + name: 'Metric1' + metricNamespace: 'microsoft.netapp/netappaccounts/capacitypools/volumes' + metricName: 'VolumeConsumedSizePercentage' + operator: 'GreaterThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes' + } + ] + virtualMachines: [ + { + name: '${AlertNamePrefix}-HP-VM-HighCPU-85-Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-High CPU 85 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Potential performance issues for users on the same host due to moderately limited CPU (Avarage over 5 mins.) Investigate session host CPU usage per user and/or CPU requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 85 + name: 'Metric1' + metricNamespace: 'microsoft.compute/virtualmachines' + metricName: 'Percentage CPU' + operator: 'GreaterThan' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'microsoft.compute/virtualmachines' + } + { + name: '${AlertNamePrefix}-HP-VM-HighCPU-95-Prcnt-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-High CPU 95 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Potential performance issues for users on the same host due to critically limited CPU (Avarage over 5 mins.) Investigate session host CPU usage per user and/or CPU requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 95 + name: 'Metric1' + metricNamespace: 'microsoft.compute/virtualmachines' + metricName: 'Percentage CPU' + operator: 'GreaterThan' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'microsoft.compute/virtualmachines' + } + { + name: '${AlertNamePrefix}-HP-VM-AvailMemLess-2GB-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-Available Memory Less Than 2GB (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Potential performance issues for users on the same host due to moderately low memory. Investigate session host memory usage per user and/or memory requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 2147483648 + name: 'Metric1' + metricNamespace: 'microsoft.compute/virtualmachines' + metricName: 'Available Memory Bytes' + operator: 'LessThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'microsoft.compute/virtualmachines' + } + { + name: '${AlertNamePrefix}-HP-VM-AvailMemLess-1GB-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-Available Memory Less Than 1GB (xHostPoolNamex)' + description: '${AlertDescriptionHeader}Potential performance issues for users on the same host due to critically low memory. Investigate session host memory usage per user and/or memory requirements and adjust if/as needed for xHostPoolNamex. Check user active vs. disconnected status.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 1073741824 + name: 'Metric1' + metricNamespace: 'microsoft.compute/virtualmachines' + metricName: 'Available Memory Bytes' + operator: 'LessThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'microsoft.compute/virtualmachines' + } + { + name: '${AlertNamePrefix}-HP-VM-OSDiskBandwidthAvg85-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-OS Disk Bandwidth Average Consumed 85 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}The OS Disk is nearing it\'s allowed IO maximum based on the Disk SKU within xHostPoolNamex. Consider review of what applications are possibly causing excessive disk activity and potentially move to a larger or premium disk SKU.' + severity: 2 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 85 + name: 'Metric1' + metricNamespace: 'microsoft.compute/virtualmachines' + dimensions: [ + { + name: 'LUN' + operator: 'Include' + values: [ + '*' + ] + } + ] + metricName: 'OS Disk Bandwidth Consumed Percentage' + operator: 'GreaterThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'microsoft.compute/virtualmachines' + } + { + name: '${AlertNamePrefix}-HP-VM-OSDiskBandwidthAvg95-xHostPoolNamex' + displayName: '${AlertNamePrefix}-HostPool-VM-OS Disk Bandwidth Average Consumed 95 Percent (xHostPoolNamex)' + description: '${AlertDescriptionHeader}The OS Disk is near it\'s allowed IO maximum based on the Disk SKU within xHostPoolNamex. Consider review of what applications are possibly causing excessive disk activity and potentially move to a larger or premium disk SKU.' + severity: 1 + evaluationFrequency: 'PT5M' + windowSize: 'PT15M' + criteria: { + allOf: [ + { + threshold: 95 + name: 'Metric1' + metricNamespace: 'microsoft.compute/virtualmachines' + dimensions: [ + { + name: 'LUN' + operator: 'Include' + values: [ + '*' + ] + } + ] + metricName: 'OS Disk Bandwidth Consumed Percentage' + operator: 'GreaterThanOrEqual' + timeAggregation: 'Average' + criterionType: 'StaticThresholdCriterion' + } + ] + 'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria' + } + targetResourceType: 'microsoft.compute/virtualmachines' + } + ] +} + +var LogAlertsSvcHealth = [ + { + name: '${AlertNamePrefix}-SerivceHealth-ServiceIssue' + displayName: '${AlertNamePrefix}-SerivceHealth-Serivice Issue' + description: AlertDescriptionHeader + anyof: [ + { + field: 'properties.incidentType' + equals: 'Incident' + } + ] + } + { + name: '${AlertNamePrefix}-SerivceHealth-PlannedMaintenance' + displayName: '${AlertNamePrefix}-SerivceHealth-Planned Maintenance' + description: AlertDescriptionHeader + anyOf: [ + { + field: 'properties.incidentType' + equals: 'Maintenance' + } + ] + } + { + name: '${AlertNamePrefix}-SerivceHealth-HealthAdvisory' + displayName: '${AlertNamePrefix}-SerivceHealth-HealthAdvisory' + description: AlertDescriptionHeader + anyOf: [ + { + field: 'properties.incidentType' + equals: 'Informational' + } + { + field: 'properties.incidentType' + equals: 'ActionRequired' + } + ] + } + { + name: '${AlertNamePrefix}-SerivceHealth-Security' + displayName: '${AlertNamePrefix}-SerivceHealth-Security' + description: AlertDescriptionHeader + anyOf: [ + { + field: 'properties.incidentType' + equals: 'Security' + } + ] + } +] +var varJobScheduleParamsHostPool = { + CloudEnvironment: CloudEnvironment + SubscriptionId: SubscriptionId + } +// fixes issue with array not being in JSON format +var varStorAcctResIDsString = StorageAccountResourceIds +var varJobScheduleParamsAzFiles = { + CloudEnvironment: CloudEnvironment + StorageAccountResourceIDs: string(varStorAcctResIDsString) +} + +var SubscriptionId = subscription().subscriptionId +var varScheduleName = 'AVD_Chk-' +var varTimeZone = varTimeZones[Location] +var varTimeZones = { + australiacentral: 'AUS Eastern Standard Time' + australiacentral2: 'AUS Eastern Standard Time' + australiaeast: 'AUS Eastern Standard Time' + australiasoutheast: 'AUS Eastern Standard Time' + brazilsouth: 'E. South America Standard Time' + brazilsoutheast: 'E. South America Standard Time' + canadacentral: 'Eastern Standard Time' + canadaeast: 'Eastern Standard Time' + centralindia: 'India Standard Time' + centralus: 'Central Standard Time' + chinaeast: 'China Standard Time' + chinaeast2: 'China Standard Time' + chinanorth: 'China Standard Time' + chinanorth2: 'China Standard Time' + eastasia: 'China Standard Time' + eastus: 'Eastern Standard Time' + eastus2: 'Eastern Standard Time' + francecentral: 'Central Europe Standard Time' + francesouth: 'Central Europe Standard Time' + germanynorth: 'Central Europe Standard Time' + germanywestcentral: 'Central Europe Standard Time' + japaneast: 'Tokyo Standard Time' + japanwest: 'Tokyo Standard Time' + jioindiacentral: 'India Standard Time' + jioindiawest: 'India Standard Time' + koreacentral: 'Korea Standard Time' + koreasouth: 'Korea Standard Time' + northcentralus: 'Central Standard Time' + northeurope: 'GMT Standard Time' + norwayeast: 'Central Europe Standard Time' + norwaywest: 'Central Europe Standard Time' + southafricanorth: 'South Africa Standard Time' + southafricawest: 'South Africa Standard Time' + southcentralus: 'Central Standard Time' + southindia: 'India Standard Time' + southeastasia: 'Singapore Standard Time' + swedencentral: 'Central Europe Standard Time' + switzerlandnorth: 'Central Europe Standard Time' + switzerlandwest: 'Central Europe Standard Time' + uaecentral: 'Arabian Standard Time' + uaenorth: 'Arabian Standard Time' + uksouth: 'GMT Standard Time' + ukwest: 'GMT Standard Time' + usdodcentral: 'Central Standard Time' + usdodeast: 'Eastern Standard Time' + usgovarizona: 'Mountain Standard Time' + usgoviowa: 'Central Standard Time' + usgovtexas: 'Central Standard Time' + usgovvirginia: 'Eastern Standard Time' + westcentralus: 'Mountain Standard Time' + westeurope: 'Central Europe Standard Time' + westindia: 'India Standard Time' + westus: 'Pacific Standard Time' + westus2: 'Pacific Standard Time' + westus3: 'Mountain Standard Time' +} + +// =========== // +// Deployments // +// =========== // + +// AVD Shared Services Resource Group +module resourceGroupAVDMetricsCreate 'carml/1.3.0/Microsoft.Resources/resourceGroups/deploy.bicep' = if (ResourceGroupCreate) { + name: ResourceGroupName + params: { + name: ResourceGroupName + location: Location + enableDefaultTelemetry: false + tags: contains(Tags, 'Microsoft.Resources/resourceGroups') ? Tags['Microsoft.Resources/resourceGroups'] : {} + } +} + +resource resourceGroupAVDMetricsExisting 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { + name: ResourceGroupName +} + +/* module identityUserManaged 'carml/1.3.0/Microsoft.ManagedIdentity/userAssignedIdentities/deploy.bicep' = { + name: 'c_UserMgId_${UsrManagedIdentityName}' + scope: resourceGroup(ResourceGroupCreate ? resourceGroupAVDMetricsCreate.name : resourceGroupAVDMetricsExisting.name) + params: { + location: Location + name: UsrManagedIdentityName + enableDefaultTelemetry: false + tags: contains(Tags, 'Microsoft.ManagedIdentity/userAssignedIdentities') ? Tags['Microsoft.ManagedIdentity/userAssignedIdentities'] : {} + } + dependsOn: ResourceGroupCreate ? [ resourceGroupAVDMetricsCreate ] : [ resourceGroupAVDMetricsExisting ] +} */ + +// Deploy new automation account +module automationAccount 'carml/1.3.0/Microsoft.Automation/automationAccounts/deploy.bicep' = { + name: 'c_AutomtnAcct-${AutomationAccountName}' + scope: resourceGroup(ResourceGroupName) + params: { + diagnosticLogCategoriesToEnable: [ + 'JobLogs' + 'JobStreams' + ] + enableDefaultTelemetry: false + diagnosticWorkspaceId: LogAnalyticsWorkspaceResourceId + name: AutomationAccountName + jobSchedules: !empty(StorageAccountResourceIds) ? [ + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-0' + } + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-1' + } + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-2' + } + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-3' + } + { + parameters: varJobScheduleParamsAzFiles + runbookName: RunbookNameGetStorage + scheduleName: '${varScheduleName}AzFilesStor-0' + } + { + parameters: varJobScheduleParamsAzFiles + runbookName: RunbookNameGetStorage + scheduleName: '${varScheduleName}AzFilesStor-1' + } + { + parameters: varJobScheduleParamsAzFiles + runbookName: RunbookNameGetStorage + scheduleName: '${varScheduleName}AzFilesStor-2' + } + { + parameters: varJobScheduleParamsAzFiles + runbookName: RunbookNameGetStorage + scheduleName: '${varScheduleName}AzFilesStor-3' + } + ] :[ + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-0' + } + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-1' + } + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-2' + } + { + parameters: varJobScheduleParamsHostPool + runbookName: RunbookNameGetHostPool + scheduleName: '${varScheduleName}HostPool-3' + } + ] + location: Location + runbooks: !empty(StorageAccountResourceIds) ? [ + { + name: RunbookNameGetHostPool + description: 'AVD Metrics Runbook for collecting related Host Pool statistics to store in Log Analytics for specified Alert Queries' + type: 'PowerShell' + uri: '${_ArtifactsLocation}${RunbookScriptGetHostPool}' + version: '1.0.0.0' + } + { + name: RunbookNameGetStorage + description: 'AVD Metrics Runbook for collecting related Azure Files storage statistics to store in Log Analytics for specified Alert Queries' + type: 'PowerShell' + uri: '${_ArtifactsLocation}${RunbookScriptGetStorage}' + version: '1.0.0.0' + } + ] : [ + { + name: RunbookNameGetHostPool + description: 'AVD Metrics Runbook for collecting related Host Pool statistics to store in Log Analytics for specified Alert Queries' + type: 'PowerShell' + uri: '${_ArtifactsLocation}${RunbookScriptGetHostPool}' + version: '1.0.0.0' + } + ] + schedules: !empty(StorageAccountResourceIds) ? [ + { + name: '${varScheduleName}HostPool-0' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT15M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}HostPool-1' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT30M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}HostPool-2' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT45M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}HostPool-3' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT60M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}AzFilesStor-0' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT15M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}AzFilesStor-1' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT30M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}AzFilesStor-2' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT45M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}AzFilesStor-3' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT60M') + TimeZone: varTimeZone + advancedSchedule: {} + } + ] :[ + { + name: '${varScheduleName}HostPool-0' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT15M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}HostPool-1' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT30M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}HostPool-2' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT45M') + TimeZone: varTimeZone + advancedSchedule: {} + } + { + name: '${varScheduleName}HostPool-3' + frequency: 'Hour' + interval: 1 + startTime: dateTimeAdd(time, 'PT60M') + TimeZone: varTimeZone + advancedSchedule: {} + } + ] + skuName: 'Free' + tags: contains(Tags, 'Microsoft.Automation/automationAccounts') ? Tags['Microsoft.Automation/automationAccounts'] : {} + systemAssignedIdentity: true + } + dependsOn: ResourceGroupCreate ? [resourceGroupAVDMetricsCreate] : [resourceGroupAVDMetricsExisting] +} + +module roleAssignment_AutoAcctDesktopRead 'carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for RG in HostPoolInfo: if(!AllResourcesSameRG) { + scope: resourceGroup(split(RG.colVMResGroup, '/')[4]) + name: 'c_DsktpRead_${split(RG.colVMResGroup, '/')[4]}' + params: { + enableDefaultTelemetry: false + principalId: automationAccount.outputs.systemAssignedPrincipalId + roleDefinitionIdOrName: 'Desktop Virtualization Reader' + principalType: 'ServicePrincipal' + resourceGroupName: split(RG.colVMResGroup, '/')[4] + } + dependsOn: [ + automationAccount + ] +}] + +module roleAssignment_AutoAcctDesktopReadSameRG 'carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = if(AllResourcesSameRG) { + scope: resourceGroup(split(AVDResourceGroupId, '/')[4]) + name: 'c_DsktpRead_${split(AVDResourceGroupId, '/')[4]}' + params: { + enableDefaultTelemetry: false + principalId: automationAccount.outputs.systemAssignedPrincipalId + roleDefinitionIdOrName: 'Desktop Virtualization Reader' + principalType: 'ServicePrincipal' + resourceGroupName: split(AVDResourceGroupId, '/')[4] + } + dependsOn: [ + automationAccount + ] +} + +module roleAssignment_LogAnalytics 'carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = { + scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4]) + name: 'c_LogContrib_${split(LogAnalyticsWorkspaceResourceId, '/')[4]}' + params: { + enableDefaultTelemetry: false + principalId: automationAccount.outputs.systemAssignedPrincipalId + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/${RoleAssignments.LogAnalyticsContributor.GUID}' + principalType: 'ServicePrincipal' + resourceGroupName: split(LogAnalyticsWorkspaceResourceId, '/')[4] + } + dependsOn: [ + automationAccount + ] +} + +module roleAssignment_Storage 'carml/1.3.0/Microsoft.Authorization/roleAssignments/resourceGroup/deploy.bicep' = [for StorAcctRG in StorAcctRGs: { + scope: resourceGroup(StorAcctRG) + name: 'c_StorAcctContrib_${StorAcctRG}' + params: { + enableDefaultTelemetry: false + principalId: automationAccount.outputs.systemAssignedPrincipalId + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/${RoleAssignments.StoreAcctContrib.GUID}' + principalType: 'ServicePrincipal' + resourceGroupName: StorAcctRG + } + dependsOn: [ + automationAccount + ] +}] + +module metricsResources 'modules/metricsResources.bicep' = { + name: 'lnk_MonitoringResourcesDeployment' + scope: resourceGroup(ResourceGroupCreate ? resourceGroupAVDMetricsCreate.name : resourceGroupAVDMetricsExisting.name) + params: { + AllResourcesSameRG: AllResourcesSameRG + AVDResourceGroupId: AVDResourceGroupId + AutoResolveAlert: AutoResolveAlert + DistributionGroup: DistributionGroup + Environment: Environment + HostPoolInfo: HostPoolInfo + HostPools: HostPools + Location: Location + LogAnalyticsWorkspaceResourceId: LogAnalyticsWorkspaceResourceId + LogAlertsHostPool: LogAlertsHostPool + LogAlertsStorage: LogAlertsStorage + LogAlertsSvcHealth: LogAlertsSvcHealth + MetricAlerts: MetricAlerts + StorageAccountResourceIds: StorageAccountResourceIds + ActionGroupName: ActionGroupName + ANFVolumeResourceIds: ANFVolumeResourceIds + Tags: Tags + } + dependsOn: [ + roleAssignment_AutoAcctDesktopRead + roleAssignment_LogAnalytics + roleAssignment_Storage + ] +} diff --git a/patterns/avd/templates/modules/anfMetric.bicep b/patterns/avd/templates/modules/anfMetric.bicep new file mode 100644 index 000000000..d8bf47ec0 --- /dev/null +++ b/patterns/avd/templates/modules/anfMetric.bicep @@ -0,0 +1,33 @@ +param AutoMitigate bool +param Enabled bool +param Environment string +param Location string +param MetricAlertsANF array +param ANFVolumeResourceID string +param ActionGroupID string +param Tags object + +module metricAlerts_VirtualMachines '../carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep' = [for i in range(0, length(MetricAlertsANF)): { + name: 'c_${MetricAlertsANF[i].name}-${split(ANFVolumeResourceID, '/')[12]}-${Environment}' + params: { + enableDefaultTelemetry: false + name: '${MetricAlertsANF[i].displayName}-${split(ANFVolumeResourceID, '/')[12]}-${Environment}' + criterias: MetricAlertsANF[i].criteria.allOf + location: 'global' + alertDescription: MetricAlertsANF[i].description + severity: MetricAlertsANF[i].severity + enabled: Enabled + scopes: [ANFVolumeResourceID] + evaluationFrequency: MetricAlertsANF[i].evaluationFrequency + windowSize: MetricAlertsANF[i].windowSize + autoMitigate: AutoMitigate + tags: contains(Tags, 'Microsoft.Insights/metricAlerts') ? Tags['Microsoft.Insights/metricAlerts'] : {} + targetResourceType: MetricAlertsANF[i].targetResourceType + targetResourceRegion: Location + actions: [ + { + actionGroupId: ActionGroupID + } + ] + } +}] diff --git a/patterns/avd/templates/modules/fileservicsmetric.bicep b/patterns/avd/templates/modules/fileservicsmetric.bicep new file mode 100644 index 000000000..84919b60d --- /dev/null +++ b/patterns/avd/templates/modules/fileservicsmetric.bicep @@ -0,0 +1,36 @@ +param AutoMitigate bool +param Enabled bool +param Environment string +param Location string +param StorageAccountResourceID string +param MetricAlertsFileShares array +param ActionGroupID string +param Tags object + +var FileServicesResourceID = '${StorageAccountResourceID}/fileServices/default' + +module metricAlerts_FileServices '../carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep' = [for i in range(0, length(MetricAlertsFileShares)): { + name: 'c_${MetricAlertsFileShares[i].name}-${split(FileServicesResourceID, '/')[8]}-${Environment}' + params: { + enableDefaultTelemetry: false + name: '${MetricAlertsFileShares[i].displayName}-${split(FileServicesResourceID, '/')[8]}-${Environment}' + criterias: MetricAlertsFileShares[i].criteria.allOf + location: 'global' + alertDescription: MetricAlertsFileShares[i].description + severity:MetricAlertsFileShares[i].severity + enabled: Enabled + scopes: [FileServicesResourceID] //Assuming first VM Resource ID has same RG for all + evaluationFrequency: MetricAlertsFileShares[i].evaluationFrequency + windowSize: MetricAlertsFileShares[i].windowSize + autoMitigate: AutoMitigate + tags: contains(Tags, 'Microsoft.Insights/metricAlerts') ? Tags['Microsoft.Insights/metricAlerts'] : {} + targetResourceType: MetricAlertsFileShares[i].targetResourceType + targetResourceRegion: Location + actions: [ + { + actionGroupId: ActionGroupID + } + ] + + } +}] diff --git a/patterns/avd/templates/modules/hostPoolAlerts.bicep b/patterns/avd/templates/modules/hostPoolAlerts.bicep new file mode 100644 index 000000000..808e9e632 --- /dev/null +++ b/patterns/avd/templates/modules/hostPoolAlerts.bicep @@ -0,0 +1,45 @@ +param AutoMitigate bool +param ActionGroupId string +param Environment string +param HostPoolName string +param LogAlertsHostPool array +param LogAnalyticsWorkspaceResourceId string +param Location string +param Tags object + +// Help ensure entire deployment name is under 64 characters +var HostPoolResourceName = length(HostPoolName) < 20 ? HostPoolName : skip(HostPoolName, length(HostPoolName)-20) + +module logAlertHostPoolQueries '../carml/1.3.0/Microsoft.Insights/scheduledQueryRules/deploy.bicep' = [for i in range(0, length(LogAlertsHostPool)): { + name: 'c_${replace(LogAlertsHostPool[i].name, 'xHostPoolNamex', HostPoolResourceName)}-${Environment}' + params: { + enableDefaultTelemetry: false + name: '${replace(LogAlertsHostPool[i].name, 'xHostPoolNamex', HostPoolResourceName)}-${Environment}' + autoMitigate: AutoMitigate + criterias: { + allOf: [ + { + query: replace(LogAlertsHostPool[i].criteria.allOf[0].query, 'xHostPoolNamex', HostPoolName) + timeAggregation: LogAlertsHostPool[i].criteria.allOf[0].timeAggregation + dimensions: LogAlertsHostPool[i].criteria.allOf[0].dimensions + operator: LogAlertsHostPool[i].criteria.allOf[0].operator + threshold: LogAlertsHostPool[i].criteria.allOf[0].threshold + failingPeriods: LogAlertsHostPool[i].criteria.allOf[0].failingPeriods + }] + } + scopes: [LogAnalyticsWorkspaceResourceId] + location: Location + actions: [ActionGroupId] + alertDisplayName: '${replace(LogAlertsHostPool[i].displayName, 'xHostPoolNamex', HostPoolResourceName)}-${Environment}' + alertDescription: '${replace(LogAlertsHostPool[i].description, 'xHostPoolNamex', HostPoolName)}-${Environment}' + enabled: false + evaluationFrequency: LogAlertsHostPool[i].evaluationFrequency + severity: LogAlertsHostPool[i].severity + tags: contains(Tags, 'Microsoft.Insights/scheduledQueryRules') ? Tags['Microsoft.Insights/scheduledQueryRules'] : {} + windowSize: LogAlertsHostPool[i].windowSize + } +}] + +output HostPoolResourceName string = HostPoolResourceName +output HostPoolName string = HostPoolName +output HostPoolNameLength int = length(HostPoolName) diff --git a/patterns/avd/templates/modules/metricAlertsVms.bicep b/patterns/avd/templates/modules/metricAlertsVms.bicep new file mode 100644 index 000000000..93a545d10 --- /dev/null +++ b/patterns/avd/templates/modules/metricAlertsVms.bicep @@ -0,0 +1,39 @@ +param AutoMitigate bool +param ActionGroupId string +param Enabled bool +param Environment string +param HostPoolName string +param MetricAlerts object +param Tags object +param Location string +param VMResourceGroupId string + +// Help ensure entire deployment name is under 64 characters +var HostPoolResourceName = length(HostPoolName) < 20 ? HostPoolName : skip(HostPoolName, length(HostPoolName)-20) + +module metricAlerts_VirtualMachines '../carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep' = [for i in range(0, length(MetricAlerts.virtualMachines)): { + name: 'c_${replace(MetricAlerts.virtualMachines[i].name, 'xHostPoolNamex', HostPoolResourceName)}-${Environment}' + params: { + enableDefaultTelemetry: false + name: '${replace(MetricAlerts.virtualMachines[i].displayName, 'xHostPoolNamex', HostPoolResourceName)}-${Environment}' + criterias: MetricAlerts.virtualMachines[i].criteria.allOf + location: 'global' + alertDescription: replace(MetricAlerts.virtualMachines[i].description, 'xHostPoolNamex', HostPoolName) + severity: MetricAlerts.virtualMachines[i].severity + enabled: Enabled + scopes: [VMResourceGroupId] + evaluationFrequency: MetricAlerts.virtualMachines[i].evaluationFrequency + windowSize: MetricAlerts.virtualMachines[i].windowSize + autoMitigate: AutoMitigate + tags: contains(Tags, 'Microsoft.Insights/metricAlerts') ? Tags['Microsoft.Insights/metricAlerts'] : {} + targetResourceType: MetricAlerts.virtualMachines[i].targetResourceType + targetResourceRegion: Location + actions: [ + { + actionGroupId: ActionGroupId + webHookProperties: {} + } + ] + + } +}] diff --git a/patterns/avd/templates/modules/metricsResources.bicep b/patterns/avd/templates/modules/metricsResources.bicep new file mode 100644 index 000000000..b7bb814a8 --- /dev/null +++ b/patterns/avd/templates/modules/metricsResources.bicep @@ -0,0 +1,200 @@ +param ActionGroupName string +param AllResourcesSameRG bool +param AutoResolveAlert bool +param AVDResourceGroupId string +param ANFVolumeResourceIds array +param DistributionGroup string +param Environment string +param HostPoolInfo array +param HostPools array +param Location string +param LogAnalyticsWorkspaceResourceId string +param LogAlertsStorage array +param LogAlertsHostPool array +param LogAlertsSvcHealth array +param MetricAlerts object +param StorageAccountResourceIds array +param Tags object + +var SubscriptionId = subscription().subscriptionId +var SubscriptionName = replace(subscription().displayName, ' ', '') + +module actionGroup '../carml/1.3.0/Microsoft.Insights/actionGroups/deploy.bicep' = { + name: ActionGroupName + params: { + emailReceivers: [ + { + emailAddress: DistributionGroup + name: 'AVD Operations Admin(s)' + useCommonAlertSchema: true + } + ] + enabled: true + location: 'global' + enableDefaultTelemetry: false + name: ActionGroupName + groupShortName: 'AVDMetrics' + tags: contains(Tags, 'Microsoft.Insights/actionGroups') ? Tags['Microsoft.Insights/actionGroups'] : {} + } +} + +// If VM and Host Pools mapped - loop through each object in Host Pool Info which will have a single HP per VM RG +module metricAlertsVms 'metricAlertsVms.bicep' = [for i in range(0, length(HostPoolInfo)): if(!AllResourcesSameRG) { + name: !AllResourcesSameRG ? 'lnk_VMMtrcAlrts_m_${split(HostPoolInfo[i].colHostPoolName, '/')[8]}' : 'lnk_VMMtrcAlrts_m_NA' + params: { + HostPoolName: !AllResourcesSameRG ? split(HostPoolInfo[i].colHostPoolName, '/')[8] : 'none' + Environment: Environment + VMResourceGroupId: HostPoolInfo[i].colVMresGroup + MetricAlerts: MetricAlerts + Enabled: false + AutoMitigate: AutoResolveAlert + Location: Location + ActionGroupId: actionGroup.outputs.resourceId + Tags: Tags + } +}] +// If all resources in same RG, loop through Host Pools +module metricAlertsVmsSingleRG 'metricAlertsVms.bicep' = [for i in range(0, length(HostPools)): if(AllResourcesSameRG) { + name: 'lnk_VMMtrcAlrts_s_${split(HostPools[i], '/')[8]}' + params: { + Environment: Environment + HostPoolName: split(HostPools[i], '/')[8] + VMResourceGroupId: AVDResourceGroupId + MetricAlerts: MetricAlerts + Enabled: false + AutoMitigate: AutoResolveAlert + Location: Location + ActionGroupId: actionGroup.outputs.resourceId + Tags: Tags + } +}] + +module storAccountMetric 'storAccountMetric.bicep' = [for i in range(0, length(StorageAccountResourceIds)): if (length(StorageAccountResourceIds) > 0) { + name: 'lnk_StrAcctMtrcAlrts_${split(StorageAccountResourceIds[i], '/')[8]}' + params: { + AutoMitigate: AutoResolveAlert + Enabled: false + Environment: Environment + Location: Location + StorageAccountResourceID: StorageAccountResourceIds[i] + MetricAlertsStorageAcct: MetricAlerts.storageAccounts + ActionGroupID: actionGroup.outputs.resourceId + Tags: Tags + } +}] + +module azureNetAppFilesMetric 'anfMetric.bicep' = [for i in range(0, length(ANFVolumeResourceIds)): if (length(ANFVolumeResourceIds) > 0) { + name: 'lnk_ANFMtrcAlrts_${split(ANFVolumeResourceIds[i], '/')[12]}' + params: { + AutoMitigate: AutoResolveAlert + Enabled: false + Environment: Environment + Location: Location + ANFVolumeResourceID: ANFVolumeResourceIds[i] + MetricAlertsANF: MetricAlerts.anf + ActionGroupID: actionGroup.outputs.resourceId + Tags: Tags + } +}] + +// If Metric Namespace contains file services ; change scopes to append default +// module to loop through each scope time as it MUST be a single Resource ID +module fileServicesMetric 'fileservicsmetric.bicep' = [for i in range(0, length(StorageAccountResourceIds)): if (length(StorageAccountResourceIds) > 0) { + name: 'lnk_FlSvcsMtrcAlrts_${i}' + params: { + AutoMitigate: AutoResolveAlert + Enabled: false + Environment: Environment + Location: Location + StorageAccountResourceID: StorageAccountResourceIds[i] + MetricAlertsFileShares: MetricAlerts.fileShares + ActionGroupID: actionGroup.outputs.resourceId + Tags: Tags + } +}] + +module logAlertStorage '../carml/1.3.0/Microsoft.Insights/scheduledQueryRules/deploy.bicep' = [for i in range(0, length(LogAlertsStorage)): { + name: 'c_${LogAlertsStorage[i].name}' + params: { + enableDefaultTelemetry: false + name: LogAlertsStorage[i].name + autoMitigate: AutoResolveAlert + criterias: LogAlertsStorage[i].criteria + scopes: [ LogAnalyticsWorkspaceResourceId ] + location: Location + actions: [ actionGroup.outputs.resourceId ] + alertDisplayName: LogAlertsStorage[i].displayName + alertDescription: LogAlertsStorage[i].description + enabled: false + evaluationFrequency: LogAlertsStorage[i].evaluationFrequency + severity: LogAlertsStorage[i].severity + tags: contains(Tags, 'Microsoft.Insights/scheduledQueryRules') ? Tags['Microsoft.Insights/scheduledQueryRules'] : {} + windowSize: LogAlertsStorage[i].windowSize + } +}] + +module logAlertHostPoolQueriesMapped 'hostPoolAlerts.bicep' = [for hostpool in HostPoolInfo: if(!AllResourcesSameRG) { + name: 'lnk_HPAlrts-${split(hostpool.colHostPoolName, '/')[8]}' + params: { + AutoMitigate: AutoResolveAlert + ActionGroupId: actionGroup.outputs.resourceId + Environment: Environment + HostPoolName: split(hostpool.colHostPoolName, '/')[8] + Location: Location + LogAlertsHostPool: LogAlertsHostPool + LogAnalyticsWorkspaceResourceId: LogAnalyticsWorkspaceResourceId + Tags: {} + } +}] + +module logAlertHostPoolQueriesSingleRG 'hostPoolAlerts.bicep' = [for hostpool in HostPools: if(AllResourcesSameRG) { + name: 'lnk_HPAlrts-${split(hostpool, '/')[8]}' + params: { + AutoMitigate: AutoResolveAlert + ActionGroupId: actionGroup.outputs.resourceId + Environment: Environment + HostPoolName: split(hostpool, '/')[8] + Location: Location + LogAlertsHostPool: LogAlertsHostPool + LogAnalyticsWorkspaceResourceId: LogAnalyticsWorkspaceResourceId + Tags: {} + } +}] + +// Currently only deploys IF Cloud Environment is Azure Commercial Cloud +module logAlertSvcHealth '../carml/1.3.0/Microsoft.Insights/activityLogAlerts/deploy.bicep' = [for i in range(0, length(LogAlertsSvcHealth)): { + name: 'c_${LogAlertsSvcHealth[i].name}' + params: { + enableDefaultTelemetry: false + name: '${LogAlertsSvcHealth[i].displayName}-${SubscriptionName}' + enabled: false + location: 'global' + tags: contains(Tags, 'Microsoft.Insights/activityLogAlerts') ? Tags['Microsoft.Insights/activityLogAlerts'] : {} + scopes: [ + '/subscriptions/${SubscriptionId}' + ] + conditions: [ + { + field: 'category' + equals: 'ServiceHealth' + } + { + anyOf: LogAlertsSvcHealth[i].anyof + } + { + field: 'properties.impactedServices[*].ServiceName' + containsAny: [ + 'Windows Virtual Desktop' + ] + } + { + field: 'properties.impactedServices[*].ImpactedRegions[*].RegionName' + containsAny: [ + Location + ] + } + ] + actions: [ actionGroup.outputs.resourceId ] + alertDescription: LogAlertsSvcHealth[i].description + } +}] diff --git a/patterns/avd/templates/modules/requirements.psd1 b/patterns/avd/templates/modules/requirements.psd1 new file mode 100644 index 000000000..1fd40985c --- /dev/null +++ b/patterns/avd/templates/modules/requirements.psd1 @@ -0,0 +1,9 @@ +# This file enables modules to be automatically managed by the Functions service. +# See https://aka.ms/functionsmanageddependency for additional information. +# +@{ + # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. + # To use the Az module in your function app, please uncomment the line below. + # 'Az' = '7.*' + 'Az.Accounts' = '2.*' +} \ No newline at end of file diff --git a/patterns/avd/templates/modules/run.ps1 b/patterns/avd/templates/modules/run.ps1 new file mode 100644 index 000000000..105e61a0f --- /dev/null +++ b/patterns/avd/templates/modules/run.ps1 @@ -0,0 +1,303 @@ +# Input bindings are passed in via param block. +param($Timer) + +# Get the current universal time in the default string format. +$currentUTCtime = (Get-Date).ToUniversalTime() + +# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled. +if ($Timer.IsPastDue) { + Write-Host "PowerShell timer is running late!" +} + +# USER DEFINED INFORMATION REQUIRED +# Initial Subscription for getting Authentication Token +# Tag used for LogAnalytics and HostPool Workspaces +$subscriptionName = $env:SubscriptionName +$subscriptionid = $env:subscriptionID +$LAWName = $env:LogAnalyticsWorkSpaceName +$resourceGroups = $env:HostPoolResourceGroupNames | convertfrom-json + +# Write an information log with the current time. +Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime" + +Function Create-AccessToken { + param($resourceURI) + + If ($null -eq $env:MSI_ENDPOINT) { + # Connect-AzAccount -Identity + $token = Get-AzAccessToken + Return $token.Token + } + Else { + $tokenAuthURI = $env:MSI_ENDPOINT + "?resource=$resourceURI&api-version=2017-09-01" + $headers = @{'Secret'="$env:MSI_SECRET"} + try { + $tokenResponse = Invoke-RestMethod -Method Get -header $headers -Uri $tokenAuthURI -ErrorAction:stop + return $tokenResponse.access_token + } + catch { + write-error "Unable to retrieve access token $error" + exit 1 + } + } +} + +Function Query-Azure { + param($query,$accesstoken) + + $url = $ManageURL + $query + $headers = @{'Authorization' = "Bearer $accessToken"} + + try { + $response = Invoke-RestMethod -Method 'Get' -Uri $url -Headers $headers -ErrorAction:stop + Return $response + } + catch { write-error "Unable to query Azure RestAPI: $error" } +} + +Function DimensionSpliter { + param($dimensiongroup) + + $dims = $dimensiongroup.split(";") + [System.Collections.Generic.List[System.Object]]$dimobject = @() + foreach ($dim in $dims) { + $obj = [pscustomobject]@{ + name = $dim.split(":")[0] + value = $dim.split(":")[1] + } + $dimobject.Add($obj) + } + return $dimobject +} + +Function Calculate-Metric { + param($metric,$sessions,$hosts,$vms,$hostpool) + + $dimvalues = DimensionSpliter $metric.Dimensions | Foreach-Object {$_.Value} + + try { [int]$imetricresult = Invoke-Command -Scriptblock $metric.Query } + catch { + Write-Warning ("Metric query failed for: [{0}] - {1}" -f $metric.Namespace,$metric.Metric) + Write-Warning ("{0}" -f $_.exception.message) + return $False + } + + $metricdata = [pscustomobject]@{ + dimValues = $dimvalues + min = $imetricresult + max = $imetricresult + sum = $imetricresult + count = 1 + } + return $metricdata +} + +Function POST-CustomMetric{ + param($custommetricjson,$accesstoken,$targetresourceid,$region) + + $url = "https://$region.monitoring.azure.com$targetresourceid/metrics" + # Write-Host "--> URL: $url" + $headers = @{'Authorization' = "Bearer $accessToken" + 'Content-Type' = "application/json"} + try { $metricapiresponse = Invoke-RestMethod -Method 'Post' -Uri $url -Headers $headers -body $custommetricjson -ErrorAction:stop } + catch { + write-warning "Unable POST metric $error" + return $false + } + return $true +} + +Function Publish-Metric{ + param($metric,$sessions,$hosts,$vms,$hostpool,$azmontoken,$targetresourceid,$region) + + $dimnames = DimensionSpliter $metric.Dimensions | Select-Object -ExpandProperty Name + + $series = [System.Collections.Generic.List[System.Object]]@() + $metricresult = Calculate-Metric $metric $sessions $hosts $vms $hostpool + + If($metricresult -eq $False) { return $False } + Else { $series.Add($metricresult) } + + $custommetric = [PSCustomObject]@{ + time = (Get-Date -Format 'o') + data = [PSCustomObject]@{ + baseData = [PSCustomObject]@{ + metric = $metric.metric + namespace = $metric.namespace + dimNames = $dimnames + series = $series + } + } + } + + $custommetricjson = $custommetric | convertto-json -depth 10 -compress + write-output ("Publishing to Azure Monitor for Namespace:{0} Metric:{1}" -f $metric.namespace,$metric.metric) + $Postresult = POST-CustomMetric $custommetricjson $azmontoken $targetresourceid $region + return $Postresult +} + +# URL(s) for creating access tokens +$Environment = (Get-AzContext).Environment.Name +$ManageURL = (Get-AzEnvironment | Where-Object {$_.Name -eq $Environment}).ResourceManagerUrl +$WVDResourceURI = $ManageURL +$AZMonResourceURI = "https://monitoring.azure.com/" + +# $WVDResourceURI = "https://management.core.windows.net/" +# $AZMonResourceURI = "https://monitoring.azure.com/" + + +Write-Output ("Creating Access Token for Azure Management ({0})" -f $WVDResourceURI) +$token = Create-AccessToken -resourceURI $WVDResourceURI +Write-Output ("Creating Access Token for Azure Monitor ({0})" -f $AZMonResourceURI) +$azmontoken = Create-AccessToken -resourceURI $AZMonResourceURI + +Write-Output ("Collecting AVD Azure Subscriptions") +$subscriptionQuery = "/subscriptions?api-version=2016-06-01" +$subscription = (Query-Azure $subscriptionQuery $token).Value.Where{$_.displayName -eq $subscriptionName} + +# foreach ($subscription in $subscriptions) { + Write-Output ("Working on '{0}' Subscription Resources" -f $subscription.displayName) + $subscriptionid = $subscription.subscriptionid + + # $resourceGroupQuery = ("/subscriptions/{0}/resourcegroups/?api-version=2019-10-01" -f $subscriptionid) + # $resourceGroups = (Query-Azure $resourceGroupQuery $token).Value.Where{$_.Tags.$tagName -eq $tagValue} + # Write-Output ("Found {0} Host Pool Resource Groups in '{1}'" -f $resourceGroups.Count,$subscription.displayName) + + $logAnalyticsQuery = ("/subscriptions/{0}/providers/Microsoft.OperationalInsights/workspaces?api-version=2015-11-01-preview" -f $subscriptionid) + $logAnalyticsWorkspace = (Query-Azure $logAnalyticsQuery $token).Value.Where{$_.name -eq $LAWName} + + <# $logAnalyticsWorkspace = (Query-Azure $logAnalyticsQuery $token).Value.Where{$_.Tags.$tagName -eq $tagValue} + If ($logAnalyticsWorkspace.Count -gt 1) { + Write-Warning ("Found {0} Log Analytics Workspaces in the {1} Subscription" -f $logAnalyticsWorkspace.Count,$subscription.displayName) + Write-Warning ("Review the Azure Query and ensure only 1 Log Analytics Workspace is returned") + Exit 1 + }#> + + If (!$logAnalyticsWorkspace) { + Write-Warning ("Unable to find a Log Analytics Workspace: $LAWName") + Exit 1 + } + Else { + # Write-Host ("-------> Log Analytics Workspace: {0}" -f $logAnalyticsWorkspace.Name) + # Write-Host ("-------> Log Query: {0}" -f $logAnalyticsQuery) + $workspaceId = $logAnalyticsWorkspace.Id + $workspaceRegion = $logAnalyticsWorkspace.Location + $workspaceName = $logAnalyticsWorkspace.Name + } + + foreach($resourceGroup in $resourceGroups) { + $resourceGroupQuery = ("/subscriptions/{0}/resourcegroups/{1}?api-version=2021-04-01" -f $subscriptionid,$resourceGroup) # Moved inside foreach + $resourceGroup = Query-Azure $resourceGroupQuery $token # Moved inside foreach + + + Write-Output ("Working on '{0}' Resources" -f $resourceGroup.Name) + $resourceGroupName = $resourceGroup.Name + $wvdapi = '2019-12-10-preview' + $hostPoolsQuery = ("/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostPools?api-version={2}" -f $subscriptionid,$resourceGroupName,$wvdapi) + $hostPools = (Query-Azure $hostPoolsQuery $token).Value + + If ($hostPools.Count -gt 0) { + Write-Output ("Found {0} Host Pool(s) in {1} Resource Group" -f $hostPools.Count,$resourceGroupName) + + foreach ($hostPool in $hostPools) { + Write-Output ("Working on '{0}' Resources" -f $hostPool.Name) + $poolName = $hostPool.Name + # $workspaceRegion = $hostPool.Location + + Write-Output ("Querying Azure for AVD Resource data (Virtual Machines, Session Hosts, Sessions)") + $sessionsquery = ("/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostPools/{2}/userSessions?api-version={3}" -f $subscriptionid,$resourceGroupName,$poolName,$wvdapi) + $hostsquery = ("/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostPools/{2}/SessionHosts?api-version={3}" -f $subscriptionid,$resourceGroupName,$poolName,$wvdapi) + $vmquery = ("/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines?api-version=2020-06-01" -f $subscriptionid,$resourceGroupName) + + $sessions = (Query-Azure $sessionsquery $token).Value + $hosts = (Query-Azure $hostsquery $token).Value + $vms = (Query-Azure $vmquery $token).Value + + Write-Output ("Creating Azure Monitor Metric Definitions") + [System.Collections.Generic.List[System.Object]]$metricDefinitions = @( + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Active Sessions" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($sessions.properties | Where-Object {$_.sessionstate -eq "Active" -AND $_.userprincipalname -ne $null}).Count } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Disconnected Sessions" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($sessions.properties | Where-Object {$_.sessionState -eq "Disconnected" -AND $_.userPrincipalName -ne $null}).Count } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Total Sessions" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($sessions.properties | Where-Object {$_.userPrincipalName -ne $null}).Count } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Draining Hosts" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($hosts.properties | Where-Object {$_.allowNewSession -eq $false}).Count } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Unhealthy Hosts" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -ne "Available"}).Count } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Healthy Hosts" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -eq "Available"}).Count } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Max Sessions in Pool" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { + $healthyHosts = ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -eq "Available"}).Count + $healthyHosts * $hostpool.properties.maxSessionLimit + } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Available Sessions in Pool" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { + $healthyHosts = ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -eq "Available"}).Count + $totalSessions = ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -eq "Available"} | Measure-Object -Property Sessions -Sum).Sum + $maxSessions = $healthyHosts * $hostpool.properties.maxSessionLimit + $maxSessions - $totalSessions + } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Session Load (%)" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { + $healthyHosts = ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -eq "Available"}).Count + $totalSessions = ($hosts.properties | Where-Object {$_.allowNewSession -eq $true -AND $_.status -eq "Available"} | Measure-Object -Property Sessions -Sum).Sum + $maxSessions = $healthyHosts * $hostpool.properties.maxSessionLimit + If ($maxSessions -eq 0) { $maxSessions } + Else { [math]::Ceiling($totalSessions / $maxSessions * 100) } + } + }, + [pscustomobject]@{ + NameSpace = "AVD" + Metric = "Session Hosts in Maintenance" + Dimensions = "Workspace:$workspacename;Pool:$poolname" + Query = { ($vms.Tags | Where-Object {$_.'WVD-Maintenance' -eq $true}).Count } + } + ) + + Foreach ($metric in $metricDefinitions) { + Write-Output ("Publishing Metric: [{0}] - {1} ({2})" -f $metric.Namespace,$metric.Metric,$workspaceRegion) + $metricPosted = Publish-Metric $metric $sessions $hosts $vms $poolName $azmontoken $workspaceId $workspaceRegion + If ($metricPosted -eq $false) { Write-Warning ("Failed to Publish Metric: [{0}] - {1}" -f $metric.Namespace,$metric.Metric) } + } + } + } + Else { Write-Warning ("No Host Pools found in {0} Resource Group" -f $resourceGroupName) } + } +# } \ No newline at end of file diff --git a/patterns/avd/templates/modules/storAccountMetric.bicep b/patterns/avd/templates/modules/storAccountMetric.bicep new file mode 100644 index 000000000..b313d7973 --- /dev/null +++ b/patterns/avd/templates/modules/storAccountMetric.bicep @@ -0,0 +1,36 @@ +param AutoMitigate bool +param Enabled bool +param Environment string +param Location string +param StorageAccountResourceID string +param MetricAlertsStorageAcct array +param ActionGroupID string +param Tags object + +module metricAlerts_StorageAcct '../carml/1.3.0/Microsoft.Insights/metricAlerts/deploy.bicep' = [for i in range(0, length(MetricAlertsStorageAcct)): { + name: 'c_${MetricAlertsStorageAcct[i].name}-${split(StorageAccountResourceID, '/')[8]}-${Environment}' + params: { + enableDefaultTelemetry: false + name: '${MetricAlertsStorageAcct[i].displayName}-${split(StorageAccountResourceID, '/')[8]}-${Environment}' + criterias: MetricAlertsStorageAcct[i].criteria.allOf + location: 'global' + alertDescription: MetricAlertsStorageAcct[i].description + severity: MetricAlertsStorageAcct[i].severity + enabled: Enabled + scopes: [StorageAccountResourceID] //Assuming first VM Resource ID has same RG for all + evaluationFrequency: MetricAlertsStorageAcct[i].evaluationFrequency + windowSize: MetricAlertsStorageAcct[i].windowSize + autoMitigate: AutoMitigate + tags: contains(Tags, 'Microsoft.Insights/metricAlerts') ? Tags['Microsoft.Insights/metricAlerts'] : {} + targetResourceType: MetricAlertsStorageAcct[i].targetResourceType + targetResourceRegion: Location + actions: [ + { + actionGroupId: ActionGroupID + } + ] + + } +}] + + diff --git a/patterns/avd/templates/modules/userAssignedManagedIdentity.bicep b/patterns/avd/templates/modules/userAssignedManagedIdentity.bicep new file mode 100644 index 000000000..5f9fd5d53 --- /dev/null +++ b/patterns/avd/templates/modules/userAssignedManagedIdentity.bicep @@ -0,0 +1,22 @@ +param Location string +param ManagedIdentityName string +param RoleDefinitionId string + +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: ManagedIdentityName + location: Location +} + +// Role Assignment for Deployment Script Contributor +// Allows least privilege for deploying deployment script resources +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(ManagedIdentityName, RoleDefinitionId, resourceGroup().id) + properties: { + roleDefinitionId: RoleDefinitionId + principalId: userAssignedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +output principalId string = userAssignedIdentity.properties.principalId +output resourceIdentifier string = userAssignedIdentity.id From 71c5fb498cc879d5967eedb2df26092095317bc1 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Thu, 25 Jan 2024 15:04:37 -0500 Subject: [PATCH 02/12] update blue button for my repo --- .../avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md b/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md index 1449a7827..161e6bac1 100644 --- a/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md +++ b/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md @@ -20,7 +20,7 @@ An AVD deployment with associated Insights configuration per the AVD Portal's AV ## Deploy via the Azure Portal UI -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fmain%2Fpatterns%2Favd%2FavdCustomUi.json) +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) ## View / Enable Alerts after deployment From da161b822d2d61ae7f3e7b2ed50f621b3c6c6524 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Fri, 26 Jan 2024 15:52:14 -0500 Subject: [PATCH 03/12] add telemetry --- ...troduction-to-deploying-the-AVD-Pattern.md | 37 - docs/content/patterns/avd/deploy/_index.md | 32 + patterns/avd/avdCustomUi.json | 951 +++++++++--------- patterns/avd/templates/deploy.bicep | 14 + .../avd/templates/modules/pid_cuaid.bicep | 1 + 5 files changed, 528 insertions(+), 507 deletions(-) delete mode 100644 docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md create mode 100644 patterns/avd/templates/modules/pid_cuaid.bicep diff --git a/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md b/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md deleted file mode 100644 index 161e6bac1..000000000 --- a/docs/content/patterns/avd/deploy/Introduction-to-deploying-the-AVD-Pattern.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Introduction to deploying the AVD Pattern -weight: 10 ---- - -## Background - -The Azure Virtual Desktop portal within Azure has a really great feature with regards to knowing the status of all things AVD via the provided Insights. However with the provided workbook it is not possible to create alerts based on what you see in Insights or having some knowledge of Log Analytics and Kusto queries. In order to provide customers with a quick list of common alerts and pre-created queries based on recommendations between the community with coordination with the Product Group, this solution was created. The alerts are intended to be disabled upon initial deployment to provide you with a method to phase in what you want to be alerted on, and verify the threshold values that work for your environment. - -## Prerequisites - -An AVD deployment with associated Insights configuration per the AVD Portal's AVD Insights Configuration workbook. -1. An Azure Tenant and Subscription -2. At least 1 Host Pool with Session Hosts deployed -3. Log Analytics Workspace for AVD with diagnostics enabled per the configuration workbook. - - Host Pool Diagnostics enabled - - Workspace Diagnostics enabled - - Data Collection Rule associated with the Log Analytics Workspace -4. Storage either Azure Files or Azure NetApp Files configured for FSLogix Profiles (optional) - -## Deploy via the Azure Portal UI - -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) - -## View / Enable Alerts after deployment - -By design the alerts solution is deployed with all alerts being disabled in order to provide administrators to view, adjust and enable them in phases or as needed. It is recommended to start with only a few to ensure the thresholds meet your needs and false alerts are not produced or an overwhelming number of alerts flood an email account. - -You can also review the Alert Action Group and adjust as needed with additional email addresses or other methods for recieving notifications. - -1. Open the [Alerts Azure Portal Page](https://portal.azure.com/#blade/Microsoft_Azure_Monitoring/AzureMonitoringBrowseBlade/alertsV2) -2. Click on the "Alert rules" section at the top of the page. -![Screenshot](../media/avdAlertRules.jpg) -3. Initially the list of alert rules may be filtered out or appear missing. Simply change the filter to include "disabled" or click the "Clear filters" option. -![Screenshot](../media/avdAlertRulesFilter.jpg) -4. Select the check box next to each you would like to enable and click "Enable" at the top of the page. -![Screenshot](../media/avdAlertRulesEnable.jpg) \ No newline at end of file diff --git a/docs/content/patterns/avd/deploy/_index.md b/docs/content/patterns/avd/deploy/_index.md index df247e277..176e9f30d 100644 --- a/docs/content/patterns/avd/deploy/_index.md +++ b/docs/content/patterns/avd/deploy/_index.md @@ -4,3 +4,35 @@ geekdocCollapseSection: true weight: 50 --- +## Background + +The Azure Virtual Desktop portal within Azure has a really great feature with regards to knowing the status of all things AVD via the provided Insights. However with the provided workbook it is not possible to create alerts based on what you see in Insights or having some knowledge of Log Analytics and Kusto queries. In order to provide customers with a quick list of common alerts and pre-created queries based on recommendations between the community with coordination with the Product Group, this solution was created. The alerts are intended to be disabled upon initial deployment to provide you with a method to phase in what you want to be alerted on, and verify the threshold values that work for your environment. + +## Prerequisites + +An AVD deployment with associated Insights configuration per the AVD Portal's AVD Insights Configuration workbook. +1. An Azure Tenant and Subscription +2. At least 1 Host Pool with Session Hosts deployed +3. Log Analytics Workspace for AVD with diagnostics enabled per the configuration workbook. + - Host Pool Diagnostics enabled + - Workspace Diagnostics enabled + - Data Collection Rule associated with the Log Analytics Workspace +4. Storage either Azure Files or Azure NetApp Files configured for FSLogix Profiles (optional) + +## Deploy via the Azure Portal UI + +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) + +## View / Enable Alerts after deployment + +By design the alerts solution is deployed with all alerts being disabled in order to provide administrators to view, adjust and enable them in phases or as needed. It is recommended to start with only a few to ensure the thresholds meet your needs and false alerts are not produced or an overwhelming number of alerts flood an email account. + +You can also review the Alert Action Group and adjust as needed with additional email addresses or other methods for recieving notifications. + +1. Open the [Alerts Azure Portal Page](https://portal.azure.com/#blade/Microsoft_Azure_Monitoring/AzureMonitoringBrowseBlade/alertsV2) +2. Click on the "Alert rules" section at the top of the page. +[Screenshot](../media/avdAlertRules.jpg) +3. Initially the list of alert rules may be filtered out or appear missing. Simply change the filter to include "disabled" or click the "Clear filters" option. +[Screenshot](../media/avdAlertRulesFilter.jpg) +4. Select the check box next to each you would like to enable and click "Enable" at the top of the page. +[Screenshot](../media/avdAlertRulesEnable.jpg) \ No newline at end of file diff --git a/patterns/avd/avdCustomUi.json b/patterns/avd/avdCustomUi.json index 6abdccd11..a16fbadf7 100644 --- a/patterns/avd/avdCustomUi.json +++ b/patterns/avd/avdCustomUi.json @@ -1,472 +1,483 @@ { - "$schema": "https://schema.management.azure.com/schemas/2021-09-09/uiFormDefinition.schema.json", - "view": { - "kind": "Form", - "properties": { - "title": "Azure Virtual Desktop LZA: Create AVD Alerts", - "steps": [ - { - "name": "basics", - "label": "Basics", - "elements": [ - { - "name": "resourceScope", - "type": "Microsoft.Common.ResourceScope", - "instanceDetailsLabel": "AVD Alerts Deployment Details", - "subscription": { - "constraints": { - "validations": [] - } - } - }, - { - "name": "HostPoolsApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/Microsoft.DesktopVirtualization/hostpools?api-version=2021-07-12')]" - } - }, - { - "name": "ResGroupsApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat(steps('basics').resourceScope.subscription.id, '/resourceGroups?api-version=2021-04-01')]" - } - }, - { - "name": "StorAcctsApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/Microsoft.Storage/storageAccounts?api-version=2022-09-01')]" - } - }, - { - "name": "LogAnalyticsApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/microsoft.operationalinsights/workspaces?api-version=2021-06-01')]" - } - } - ] - }, - { - "name": "AlertsConfig", - "label": "Alerts Configuration", - "elements": [ - { - "name": "optionCustomScriptLocation", - "type": "Microsoft.Common.CheckBox", - "label": "Using Custom Script Location", - "toolTip": "Selecting this will provide additional feilds for inputting a Blob Storage URL and Sas Token. Currently the deployment uses the Internet facing GitHub site. This is common when deploying in Air Gapped Clouds.", - "constraints": { - "required": false, - "validationMessage": "Selecting this will provide additional feilds for inputting a Blob Storage URL and Sas Token. Currently the deployment uses the Internet facing GitHub site." - } - }, - { - "name": "CustomScriptInfo", - "type": "Microsoft.Common.Section", - "label": "Custom Script Information", - "elements": [ - { - "name": "_ArtifactsLocation", - "type": "Microsoft.Common.TextBox", - "label": "Automation Account Scripts Location", - "subLabel": "", - "toolTip": "Blob Storage Location/ URL container with Get-HostPoolInfo.ps1 and Get-StorAcctInfov2.ps1 needed for deployment and automation account setup.", - "constraints": { - "required": false, - "regex": "", - "validationMessage": "" - }, - "visible": true - }, - { - "name": "_ArtifactsLocationSasToken", - "type": "Microsoft.Common.PasswordBox", - "label": { - "password": "Artifacts Location Sas Token", - "confirmPassword": "Confirm Sas Token" - }, - "toolTip": "SaS token if needed for script location. Be sure to include the ?sp= prefix.", - "constraints": { - "required": false, - "regex": "", - "validationMessage": "" - }, - "options": { - "hideConfirmation": true - }, - "visible": true - }, - { - "name": "infoMessageCustomScriptsLoc", - "type": "Microsoft.Common.InfoBox", - "visible": true, - "options": { - "text": "Be sure that the scripts Get-HostPoolInfo.ps1 and Get-StorAcctInfov2.ps1 are copied to the Selected Storage Location. IF using a Sas Token be sure to include the ?sp= prefix!", - "style": "Info" - } - } - ], - "visible": "[equals(steps('AlertsConfig').optionCustomScriptLocation, true)]" - }, - { - "name": "ResourceGroupStatus", - "type": "Microsoft.Common.OptionsGroup", - "label": "Use New or Existing Resource Group", - "toolTip": "This will be the Resource Group in which AVD Alerts resources will be deployed in.", - "constraints": { - "allowedValues": [ - { - "label": "New", - "value": "New" - }, - { - "label": "Existing", - "value": "Existing" - } - ], - "required": true - }, - "visible": true - }, - { - "name": "resourceGroupNameNew", - "type": "Microsoft.Common.TextBox", - "label": "Resource Group Name for AVD Alerts", - "subLabel": "", - "defaultValue": "rg-AVDAlerts", - "toolTip": "AVD Alerts Resource Group where alerts based resources will be deployed.", - "constraints": { - "required": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'New')]", - "regex": "", - "validationMessage": "" - }, - "visible": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'New')]" - }, - { - "name": "resourceGroupNameExisting", - "type": "Microsoft.Common.DropDown", - "label": "Existing Resource Group", - "multiselect": false, - "defaultValue": "", - "toolTip": "AVD Alerts Resource Group where alerts based resources will be deployed.", - "filter": true, - "filterPlaceholder": "Filter Resource Groups...", - "defaultDescription": "A value for selection", - "constraints": { - "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": true - }, - "visible": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'Existing')]" - }, - { - "name": "AlertNamePrefix", - "type": "Microsoft.Common.TextBox", - "label": "Alert Name Prefix", - "subLabel": "", - "defaultValue": "AVD", - "toolTip": "Alert Name Prefix of 1 to 5 characters. (Dash will be added after prefix for you.)", - "constraints": { - "required": true, - "regex": "^([a-zA-Z0-9_-]){1,5}$", - "validationMessage": "Must be 1-5 characters." - }, - "visible": true - }, - { - "name": "DistributionGroup", - "type": "Microsoft.Common.TextBox", - "label": "User Email or Distribution Group", - "subLabel": "", - "defaultValue": "", - "toolTip": "The Email Distribution Group that will receive email alerts for AVD.", - "constraints": { - "required": true, - "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", - "validationMessage": "Email is not valid. Please re-enter." - }, - "visible": true - }, - { - "name": "Environment", - "type": "Microsoft.Common.DropDown", - "label": "Environment Type", - "defaultValue": "t", - "toolTip": "The environment is which these resources will be deployed, i.e. Test, Production, Development. This will yield a first letter desigation in some of the resource naming.", - "constraints": { - "required": true, - "allowedValues": [ - { - "label": "Development", - "value": "d" - }, - { - "label": "Production", - "value": "p" - }, - { - "label": "Test", - "value": "t" - } - ] - }, - "visible": true - }, - { - "name": "AutoResolveAlert", - "type": "Microsoft.Common.DropDown", - "label": "Allow Alerts to Auto-Resolve", - "defaultValue": "Yes", - "toolTip": "This option determines if the alert will automatically set the flag to resolved if a subsequent check is within the defined threshold.", - "constraints": { - "required": true, - "allowedValues": [ - { - "label": "Yes", - "value": true - }, - { - "label": "No", - "value": false - } - ] - }, - "visible": true - }, - { - "name": "LogAnalyticsWorkspaceResource", - "type": "Microsoft.Solutions.ResourceSelector", - "label": "Insights Log Analytics Workspace", - "toolTip": "Log Analytics Workspace in which AVD Insigts and diagnostics data resides in.", - "resourceType": "Microsoft.OperationalInsights/workspaces", - "constraints": { - "required": true - }, - "infoMessages": [], - "visible": true - }, - { - "name": "optionVMMetrics", - "type": "Microsoft.Common.CheckBox", - "label": "VMs in separate Resource Group(s)", - "constraints": { - "required": false, - "validationMessage": "Selecting this will determine if multiple VM Metric based Alerts can be deployed given the scope for VM resources is per Resource Group." - } - }, - { - "name": "AVDResourceGroupId", - "type": "Microsoft.Common.DropDown", - "label": "AVD Resource Group", - "multiselect": false, - "defaultValue": "[]", - "selectAll": false, - "toolTip": "The Resource Group where all AVD resources are deployed to include VMs.", - "filter": true, - "filterPlaceholder": "Filter Resource Groups...", - "defaultDescription": "A value for selection", - "constraints": { - "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" - }, - "visible": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" - }, - { - "name": "HostPools", - "type": "Microsoft.Common.DropDown", - "label": "Host Pools", - "multiselect": true, - "selectAll": true, - "defaultValue": "[]", - "toolTip": "Select Host Pool(s) to configure Alerts for.", - "filter": true, - "filterPlaceholder": "Filter Host Pools...", - "defaultDescription": "A value for selection", - "constraints": { - "allowedValues": "[map(steps('basics').HostPoolsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" - }, - "visible": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" - }, - { - "name": "hostPoolInfo", - "type": "Microsoft.Common.EditableGrid", - "visible": "[if(steps('AlertsConfig').optionVMMetrics, true, false)]", - "ariaLabel": "Host Pool to VM Resource Mapping", - "label": "HostPool", - "constraints": { - "width": "Full", - "rows": { - "count": { - "min": 1, - "max": 20 - } - }, - "columns": [ - { - "id": "colHostPoolName", - "header": "Host Pool", - "width": "1fr", - "element": { - "type": "Microsoft.Common.DropDown", - "placeholder": "", - "constraints": { - "allowedValues": "[map(steps('basics').HostPoolsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": true - } - } - }, - { - "id": "colVMresGroup", - "header": "VM Resource Group", - "width": "1fr", - "element": { - "type": "Microsoft.Common.DropDown", - "placeholder": "", - "constraints": { - "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": true - } - } - } - ] - } - }, - { - "name": "optionAzFiles", - "type": "Microsoft.Common.CheckBox", - "label": "Configure Alerts for Azure Files", - "constraints": { - "required": false, - "validationMessage": "Selecting this option will prompt for Storage Account information." - } - }, - { - "name": "optionANFVolumes", - "type": "Microsoft.Common.CheckBox", - "label": "Configure Alerts for Azure NetApp Volumes", - "constraints": { - "required": false, - "validationMessage": "Selecting this option will prompt for NetApp information." - } - }, - { - "name": "AzFilesStorageSection", - "type": "Microsoft.Common.Section", - "label": "Azure Files Storage", - "elements": [ - { - "name": "StorageAccountResourceIds", - "type": "Microsoft.Common.DropDown", - "label": "AVD Related Storage Accounts", - "multiselect": true, - "selectAll": true, - "defaultValue": "[]", - "toolTip": "The Storage Accounts that are used for FSLogix or MSIX App attach.", - "filterPlaceholder": "Filter Storage Accounts...", - "defaultDescription": "A value for selection", - "constraints": { - "allowedValues": "[map(steps('basics').StorAcctsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": true - }, - "visible": true - } - ], - "visible": "[steps('AlertsConfig').optionAzFiles]" - }, - { - "name": "ANFStorageSection", - "type": "Microsoft.Common.Section", - "label": "Azure NetApp Storage", - "elements": [ - { - "name": "ANFPool", - "type": "Microsoft.Solutions.ResourceSelector", - "label": "Azure NetApp Files Capacity Pool", - "toolTip": "Provide the ANF Capacity Pool housing the ANF Volumes for AVD.", - "resourceType": "Microsoft.NetApp/netAppAccounts/capacityPools", - "constraints": { - "required": true - }, - "visible": true - }, - { - "name": "ANFVolumesApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat(steps('AlertsConfig').ANFStorageSection.ANFPool.id, '/volumes?api-version=2022-09-01')]" - } - }, - { - "name": "ANFVolumeResourceIds", - "type": "Microsoft.Common.DropDown", - "label": "ANF Volumes used for AVD", - "multiselect": true, - "defaultValue": "[]", - "selectAll": true, - "toolTip": "The NetApp Volumes that are used for FSLogix or MSIX App attach.", - "filterPlaceholder": "Filter ANF Volumes...", - "defaultDescription": "A value for selection", - "constraints": { - "allowedValues": "[map(steps('AlertsConfig').ANFStorageSection.ANFVolumesApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", - "required": true - }, - "visible": true - } - ], - "visible": "[steps('AlertsConfig').optionANFVolumes]" - }, - { - "name": "Tags", - "type": "Microsoft.Common.TagsByResource", - "resources": [ - "Microsoft.Resources/resourceGroups", - "Microsoft.Automation/automationAccounts", - "Microsoft.Insights/diagnosticsettings", - "Microsoft.ManagedIdentity/userAssignedIdentities", - "Microsoft.Authorization/roleAssignments", - "Microsoft.Insights/actionGroups", - "Microsoft.Resources/deploymentScripts", - "Microsoft.Insights/metricAlerts", - "Microsoft.Insights/scheduledQueryRules", - "Microsoft.Automation/automationAccounts/runbooks", - "Microsoft.Logic/workflows" - ], - "visible": true - } - ] - } - ] - }, - "outputs": { - "parameters": { - "_ArtifactsLocation": "[steps('AlertsConfig').CustomScriptInfo._ArtifactsLocation]", - "_ArtifactsLocationSasToken": "[steps('AlertsConfig').CustomScriptInfo._ArtifactsLocationSasToken]", - "AlertNamePrefix": "[steps('AlertsConfig').AlertNamePrefix]", - "AllResourcesSameRG": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]", - "AutoResolveAlert": "[steps('AlertsConfig').AutoResolveAlert]", - "AVDResourceGroupId": "[steps('AlertsConfig').AVDResourceGroupId]", - "DistributionGroup": "[steps('AlertsConfig').DistributionGroup]", - "Environment": "[steps('AlertsConfig').Environment]", - "HostPoolInfo": "[steps('AlertsConfig').hostPoolInfo]", - "HostPools": "[steps('AlertsConfig').HostPools]", - "LogAnalyticsWorkspaceResourceId": "[steps('AlertsConfig').LogAnalyticsWorkspaceResource.id]", - "ResourceGroupName": "[if(equals(steps('AlertsConfig').ResourceGroupStatus, 'New'), steps('AlertsConfig').resourceGroupNameNew, last(split(steps('AlertsConfig').resourceGroupNameExisting, '/')))]", - "ResourceGroupStatus": "[steps('AlertsConfig').ResourceGroupStatus]", - "StorageAccountResourceIds": "[steps('AlertsConfig').AzFilesStorageSection.StorageAccountResourceIds]", - "ANFVolumeResourceIds": "[steps('AlertsConfig').ANFStorageSection.ANFVolumeResourceIds]", - "Tags": "[steps('AlertsConfig').Tags]" - }, - "kind": "Subscription", - "location": "[steps('basics').resourceScope.location.name]", - "subscriptionId": "[steps('basics').resourceScope.subscription.id]" - } - } + "$schema": "https://schema.management.azure.com/schemas/2021-09-09/uiFormDefinition.schema.json", + "view": { + "kind": "Form", + "properties": { + "title": "Azure Virtual Desktop LZA: Create AVD Alerts", + "steps": [ + { + "name": "basics", + "label": "Basics", + "elements": [ + { + "name": "resourceScope", + "type": "Microsoft.Common.ResourceScope", + "instanceDetailsLabel": "AVD Alerts Deployment Details", + "subscription": { + "constraints": { + "validations": [] + } + } + }, + { + "name": "HostPoolsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/Microsoft.DesktopVirtualization/hostpools?api-version=2021-07-12')]" + } + }, + { + "name": "ResGroupsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/resourceGroups?api-version=2021-04-01')]" + } + }, + { + "name": "StorAcctsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/Microsoft.Storage/storageAccounts?api-version=2022-09-01')]" + } + }, + { + "name": "LogAnalyticsApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').resourceScope.subscription.id, '/providers/microsoft.operationalinsights/workspaces?api-version=2021-06-01')]" + } + } + ] + }, + { + "name": "AlertsConfig", + "label": "Alerts Configuration", + "elements": [ + { + "name": "optionCustomScriptLocation", + "type": "Microsoft.Common.CheckBox", + "label": "Using Custom Script Location", + "toolTip": "Selecting this will provide additional feilds for inputting a Blob Storage URL and Sas Token. Currently the deployment uses the Internet facing GitHub site. This is common when deploying in Air Gapped Clouds.", + "constraints": { + "required": false, + "validationMessage": "Selecting this will provide additional feilds for inputting a Blob Storage URL and Sas Token. Currently the deployment uses the Internet facing GitHub site." + } + }, + { + "name": "optoutTelemetry", + "type": "Microsoft.Common.CheckBox", + "label": "Opt-Out of Telemetry", + "toolTip": "Selecting this will omit a flag that helps Microsoft track the number of times this solution was deployed. No other information is sent or monitored! This is helpful for guaging the value the solution provides for future Azure features.", + "constraints": { + "required": false, + "validationMessage": "" + } + }, + { + "name": "CustomScriptInfo", + "type": "Microsoft.Common.Section", + "label": "Custom Script Information", + "elements": [ + { + "name": "_ArtifactsLocation", + "type": "Microsoft.Common.TextBox", + "label": "Automation Account Scripts Location", + "subLabel": "", + "toolTip": "Blob Storage Location/ URL container with Get-HostPoolInfo.ps1 and Get-StorAcctInfov2.ps1 needed for deployment and automation account setup.", + "constraints": { + "required": false, + "regex": "", + "validationMessage": "" + }, + "visible": true + }, + { + "name": "_ArtifactsLocationSasToken", + "type": "Microsoft.Common.PasswordBox", + "label": { + "password": "Artifacts Location Sas Token", + "confirmPassword": "Confirm Sas Token" + }, + "toolTip": "SaS token if needed for script location. Be sure to include the ?sp= prefix.", + "constraints": { + "required": false, + "regex": "", + "validationMessage": "" + }, + "options": { + "hideConfirmation": true + }, + "visible": true + }, + { + "name": "infoMessageCustomScriptsLoc", + "type": "Microsoft.Common.InfoBox", + "visible": true, + "options": { + "text": "Be sure that the scripts Get-HostPoolInfo.ps1 and Get-StorAcctInfov2.ps1 are copied to the Selected Storage Location. IF using a Sas Token be sure to include the ?sp= prefix!", + "style": "Info" + } + } + ], + "visible": "[equals(steps('AlertsConfig').optionCustomScriptLocation, true)]" + }, + { + "name": "ResourceGroupStatus", + "type": "Microsoft.Common.OptionsGroup", + "label": "Use New or Existing Resource Group", + "toolTip": "This will be the Resource Group in which AVD Alerts resources will be deployed in.", + "constraints": { + "allowedValues": [ + { + "label": "New", + "value": "New" + }, + { + "label": "Existing", + "value": "Existing" + } + ], + "required": true + }, + "visible": true + }, + { + "name": "resourceGroupNameNew", + "type": "Microsoft.Common.TextBox", + "label": "Resource Group Name for AVD Alerts", + "subLabel": "", + "defaultValue": "rg-AVDAlerts", + "toolTip": "AVD Alerts Resource Group where alerts based resources will be deployed.", + "constraints": { + "required": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'New')]", + "regex": "", + "validationMessage": "" + }, + "visible": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'New')]" + }, + { + "name": "resourceGroupNameExisting", + "type": "Microsoft.Common.DropDown", + "label": "Existing Resource Group", + "multiselect": false, + "defaultValue": "", + "toolTip": "AVD Alerts Resource Group where alerts based resources will be deployed.", + "filter": true, + "filterPlaceholder": "Filter Resource Groups...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + }, + "visible": "[equals(steps('AlertsConfig').ResourceGroupStatus, 'Existing')]" + }, + { + "name": "AlertNamePrefix", + "type": "Microsoft.Common.TextBox", + "label": "Alert Name Prefix", + "subLabel": "", + "defaultValue": "AVD", + "toolTip": "Alert Name Prefix of 1 to 5 characters. (Dash will be added after prefix for you.)", + "constraints": { + "required": true, + "regex": "^([a-zA-Z0-9_-]){1,5}$", + "validationMessage": "Must be 1-5 characters." + }, + "visible": true + }, + { + "name": "DistributionGroup", + "type": "Microsoft.Common.TextBox", + "label": "User Email or Distribution Group", + "subLabel": "", + "defaultValue": "", + "toolTip": "The Email Distribution Group that will receive email alerts for AVD.", + "constraints": { + "required": true, + "regex": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", + "validationMessage": "Email is not valid. Please re-enter." + }, + "visible": true + }, + { + "name": "Environment", + "type": "Microsoft.Common.DropDown", + "label": "Environment Type", + "defaultValue": "t", + "toolTip": "The environment is which these resources will be deployed, i.e. Test, Production, Development. This will yield a first letter desigation in some of the resource naming.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Development", + "value": "d" + }, + { + "label": "Production", + "value": "p" + }, + { + "label": "Test", + "value": "t" + } + ] + }, + "visible": true + }, + { + "name": "AutoResolveAlert", + "type": "Microsoft.Common.DropDown", + "label": "Allow Alerts to Auto-Resolve", + "defaultValue": "Yes", + "toolTip": "This option determines if the alert will automatically set the flag to resolved if a subsequent check is within the defined threshold.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Yes", + "value": true + }, + { + "label": "No", + "value": false + } + ] + }, + "visible": true + }, + { + "name": "LogAnalyticsWorkspaceResource", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Insights Log Analytics Workspace", + "toolTip": "Log Analytics Workspace in which AVD Insigts and diagnostics data resides in.", + "resourceType": "Microsoft.OperationalInsights/workspaces", + "constraints": { + "required": true + }, + "infoMessages": [], + "visible": true + }, + { + "name": "optionVMMetrics", + "type": "Microsoft.Common.CheckBox", + "label": "VMs in separate Resource Group(s)", + "constraints": { + "required": false, + "validationMessage": "Selecting this will determine if multiple VM Metric based Alerts can be deployed given the scope for VM resources is per Resource Group." + } + }, + { + "name": "AVDResourceGroupId", + "type": "Microsoft.Common.DropDown", + "label": "AVD Resource Group", + "multiselect": false, + "defaultValue": "[]", + "selectAll": false, + "toolTip": "The Resource Group where all AVD resources are deployed to include VMs.", + "filter": true, + "filterPlaceholder": "Filter Resource Groups...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + "visible": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + { + "name": "HostPools", + "type": "Microsoft.Common.DropDown", + "label": "Host Pools", + "multiselect": true, + "selectAll": true, + "defaultValue": "[]", + "toolTip": "Select Host Pool(s) to configure Alerts for.", + "filter": true, + "filterPlaceholder": "Filter Host Pools...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').HostPoolsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + "visible": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]" + }, + { + "name": "hostPoolInfo", + "type": "Microsoft.Common.EditableGrid", + "visible": "[if(steps('AlertsConfig').optionVMMetrics, true, false)]", + "ariaLabel": "Host Pool to VM Resource Mapping", + "label": "HostPool", + "constraints": { + "width": "Full", + "rows": { + "count": { + "min": 1, + "max": 20 + } + }, + "columns": [ + { + "id": "colHostPoolName", + "header": "Host Pool", + "width": "1fr", + "element": { + "type": "Microsoft.Common.DropDown", + "placeholder": "", + "constraints": { + "allowedValues": "[map(steps('basics').HostPoolsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + } + } + }, + { + "id": "colVMresGroup", + "header": "VM Resource Group", + "width": "1fr", + "element": { + "type": "Microsoft.Common.DropDown", + "placeholder": "", + "constraints": { + "allowedValues": "[map(steps('basics').ResGroupsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + } + } + } + ] + } + }, + { + "name": "optionAzFiles", + "type": "Microsoft.Common.CheckBox", + "label": "Configure Alerts for Azure Files", + "constraints": { + "required": false, + "validationMessage": "Selecting this option will prompt for Storage Account information." + } + }, + { + "name": "optionANFVolumes", + "type": "Microsoft.Common.CheckBox", + "label": "Configure Alerts for Azure NetApp Volumes", + "constraints": { + "required": false, + "validationMessage": "Selecting this option will prompt for NetApp information." + } + }, + { + "name": "AzFilesStorageSection", + "type": "Microsoft.Common.Section", + "label": "Azure Files Storage", + "elements": [ + { + "name": "StorageAccountResourceIds", + "type": "Microsoft.Common.DropDown", + "label": "AVD Related Storage Accounts", + "multiselect": true, + "selectAll": true, + "defaultValue": "[]", + "toolTip": "The Storage Accounts that are used for FSLogix or MSIX App attach.", + "filterPlaceholder": "Filter Storage Accounts...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('basics').StorAcctsApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "visible": "[steps('AlertsConfig').optionAzFiles]" + }, + { + "name": "ANFStorageSection", + "type": "Microsoft.Common.Section", + "label": "Azure NetApp Storage", + "elements": [ + { + "name": "ANFPool", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Azure NetApp Files Capacity Pool", + "toolTip": "Provide the ANF Capacity Pool housing the ANF Volumes for AVD.", + "resourceType": "Microsoft.NetApp/netAppAccounts/capacityPools", + "constraints": { + "required": true + }, + "visible": true + }, + { + "name": "ANFVolumesApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('AlertsConfig').ANFStorageSection.ANFPool.id, '/volumes?api-version=2022-09-01')]" + } + }, + { + "name": "ANFVolumeResourceIds", + "type": "Microsoft.Common.DropDown", + "label": "ANF Volumes used for AVD", + "multiselect": true, + "defaultValue": "[]", + "selectAll": true, + "toolTip": "The NetApp Volumes that are used for FSLogix or MSIX App attach.", + "filterPlaceholder": "Filter ANF Volumes...", + "defaultDescription": "A value for selection", + "constraints": { + "allowedValues": "[map(steps('AlertsConfig').ANFStorageSection.ANFVolumesApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "visible": "[steps('AlertsConfig').optionANFVolumes]" + }, + { + "name": "Tags", + "type": "Microsoft.Common.TagsByResource", + "resources": [ + "Microsoft.Resources/resourceGroups", + "Microsoft.Automation/automationAccounts", + "Microsoft.Insights/diagnosticsettings", + "Microsoft.ManagedIdentity/userAssignedIdentities", + "Microsoft.Authorization/roleAssignments", + "Microsoft.Insights/actionGroups", + "Microsoft.Resources/deploymentScripts", + "Microsoft.Insights/metricAlerts", + "Microsoft.Insights/scheduledQueryRules", + "Microsoft.Automation/automationAccounts/runbooks", + "Microsoft.Logic/workflows" + ], + "visible": true + } + ] + } + ] + }, + "outputs": { + "parameters": { + "_ArtifactsLocation": "[steps('AlertsConfig').CustomScriptInfo._ArtifactsLocation]", + "_ArtifactsLocationSasToken": "[steps('AlertsConfig').CustomScriptInfo._ArtifactsLocationSasToken]", + "optoutTelemetry": "[steps('AlertsConfig').optoutTelemetry]", + "AlertNamePrefix": "[steps('AlertsConfig').AlertNamePrefix]", + "AllResourcesSameRG": "[if(steps('AlertsConfig').optionVMMetrics, false, true)]", + "AutoResolveAlert": "[steps('AlertsConfig').AutoResolveAlert]", + "AVDResourceGroupId": "[steps('AlertsConfig').AVDResourceGroupId]", + "DistributionGroup": "[steps('AlertsConfig').DistributionGroup]", + "Environment": "[steps('AlertsConfig').Environment]", + "HostPoolInfo": "[steps('AlertsConfig').hostPoolInfo]", + "HostPools": "[steps('AlertsConfig').HostPools]", + "LogAnalyticsWorkspaceResourceId": "[steps('AlertsConfig').LogAnalyticsWorkspaceResource.id]", + "ResourceGroupName": "[if(equals(steps('AlertsConfig').ResourceGroupStatus, 'New'), steps('AlertsConfig').resourceGroupNameNew, last(split(steps('AlertsConfig').resourceGroupNameExisting, '/')))]", + "ResourceGroupStatus": "[steps('AlertsConfig').ResourceGroupStatus]", + "StorageAccountResourceIds": "[steps('AlertsConfig').AzFilesStorageSection.StorageAccountResourceIds]", + "ANFVolumeResourceIds": "[steps('AlertsConfig').ANFStorageSection.ANFVolumeResourceIds]", + "Tags": "[steps('AlertsConfig').Tags]" + }, + "kind": "Subscription", + "location": "[steps('basics').resourceScope.location.name]", + "subscriptionId": "[steps('basics').resourceScope.subscription.id]" + } + } } \ No newline at end of file diff --git a/patterns/avd/templates/deploy.bicep b/patterns/avd/templates/deploy.bicep index 6b8454d5a..daa3fd895 100644 --- a/patterns/avd/templates/deploy.bicep +++ b/patterns/avd/templates/deploy.bicep @@ -12,6 +12,9 @@ param _ArtifactsLocation string = 'https://raw.githubusercontent.com/Azure/avdac @secure() param _ArtifactsLocationSasToken string = '' +@description('Telemetry Opt-Out') // Change this to true to opt out of Microsoft Telemetry +param optoutTelemetry bool = false + @description('Alert Name Prefix (Dash will be added after prefix for you.)') param AlertNamePrefix string = 'AVD' @@ -100,6 +103,8 @@ var RoleAssignments = { // '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor // '92aaf0da-9dab-42b6-94a3-d43ce8d16293' // Log Analtyics Contributor - allows writing to workspace for Host Pool and Storage Logic Apps +var cuaid = 'b8b4a533-1bb2-402f-bbd9-3055d00d885a' + var LogAlertsHostPool = [ {// Based on Runbook script Output to LAW name: '${AlertNamePrefix}-HP-Cap-85Prcnt-xHostPoolNamex' @@ -1956,9 +1961,18 @@ var varTimeZones = { westus3: 'Mountain Standard Time' } +var deploymentNames = { + pidCuaDeploymentName: take('pid-${cuaid}-${uniqueString(Location, subscription().displayName, time)}', 64) +} + // =========== // // Deployments // // =========== // +module deploymentNames_pidCuaDeployment 'modules/pid_cuaid.bicep' = if (!optoutTelemetry) { + name: deploymentNames.pidCuaDeploymentName + params: {} +} + // AVD Shared Services Resource Group module resourceGroupAVDMetricsCreate 'carml/1.3.0/Microsoft.Resources/resourceGroups/deploy.bicep' = if (ResourceGroupCreate) { diff --git a/patterns/avd/templates/modules/pid_cuaid.bicep b/patterns/avd/templates/modules/pid_cuaid.bicep new file mode 100644 index 000000000..567b460e6 --- /dev/null +++ b/patterns/avd/templates/modules/pid_cuaid.bicep @@ -0,0 +1 @@ +targetScope = 'subscription' From 82bd5b904ea8ed7815a718e9cca600559c3e79a0 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Sun, 4 Feb 2024 13:01:25 -0500 Subject: [PATCH 04/12] Update AVD alert names and deployment links --- docs/content/patterns/avd/_index.md | 10 +++++----- docs/content/patterns/avd/deploy/_index.md | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/content/patterns/avd/_index.md b/docs/content/patterns/avd/_index.md index 46629a736..718346778 100644 --- a/docs/content/patterns/avd/_index.md +++ b/docs/content/patterns/avd/_index.md @@ -16,7 +16,7 @@ Table below shows the Alert Names however the number of alert rules created may | Name | Threshold(s) (Severity) | Signal Type | Frequency | # Alert Rules | |--- |--- |--- |--- |--- -| AVD-HostPool-Capacity :one: | 95% (1) / 85% (2) / 50% (3)| Log Analytics | 5 min | 3/hostpool | +| AVD-HostPool-Capacity (1.) | 95% (1) / 85% (2) / 50% (3)| Log Analytics | 5 min | 3/hostpool | | AVD-HostPool-Disconnected User over n Hours (hostpoolname) | 24 (1) / 72 (2) | Log Analytics | 1 hour | 2/hostpool | | AVD-HostPool-No Resources Available (hostpoolname) | Any are Sev1 | Log Analytics | 15 min | 1/hostpool | | AVD-HostPool-VM-Available Memory Less Than nGB (hostpoolname) | 1gb (Sev1) / 2gb (Sev2) | Metric Alerts | 5 min | 2/hostpool | @@ -33,10 +33,10 @@ Table below shows the Alert Names however the number of alert rules created may | AVD-HostPool-VM-User Connection Failed (hostpoolname) | Any are Sev 3 | Log Analytics | 15 min | 1/hostpool | | AVD-HostPool-VM-Missing Critical Updates (hostpoolname) | Any are Sev 1 | Log Analytics | 1 day | 1/hostpool | | AVD-Storage-Low Space on ANF Share-XX Percent Remaining-{volumename} | 5 / 15 | Metric Alerts | 1 hour | 2/vol | -| AVD-Storage-Low Space on Azure File Share-X% Remaining-{volumename} :one: | 5 / 15 | Log Analytics | 1 hour | 2/share | +| AVD-Storage-Low Space on Azure File Share-X% Remaining-{volumename} (1.) | 5 / 15 | Log Analytics | 1 hour | 2/share | | AVD-Storage-Over XXms Latency for Storage Act-{storacctname} | 100ms / 50ms | Metric Alerts | 15 min | 2/stor acct | | AVD-Storage-Over XXms Latency Between Client-Storage-{storacctname} | 100ms / 50ms | Metric Alerts | 15 min | 2/stor acct | -| AVD-Storage-Possible Throttling Due to High IOPs-{storacctname} | na / custom :two: | Metric Alerts | 15 min | 1/stor acct | +| AVD-Storage-Possible Throttling Due to High IOPs-{storacctname} (2.) | na / custom :two: | Metric Alerts | 15 min | 1/stor acct | | AVD-Storage-Azure Files Availability-{storacctname} | 99 / na | Metric Alerts | 5 min | 1/stor acct | | AVD-ServiceHealth-Health Advisory | na | Service Health | na | 4 | | AVD-ServiceHealth-Planned Maintenance | na | Service Health | na | 4 | @@ -44,8 +44,8 @@ Table below shows the Alert Names however the number of alert rules created may | AVD-ServiceHealth-Service Issue | na | Service Health | na | 4 | **NOTES:** -:one: Alert based on associated Automation Account / Runbook -:two: See the following for custom condition. Note that both Standard and Premium values are incorporated into the alert rule. ['How to create an alert if a file share is throttled'](https://docs.microsoft.com/azure/storage/files/storage-troubleshooting-files-performance#how-to-create-an-alert-if-a-file-share-is-throttled) +1. Alert based on associated Automation Account / Runbook +2. See the following for custom condition. Note that both Standard and Premium values are incorporated into the alert rule. ['How to create an alert if a file share is throttled'](https://docs.microsoft.com/azure/storage/files/storage-troubleshooting-files-performance#how-to-create-an-alert-if-a-file-share-is-throttled) Service Health - The alert severity cannot be set or changed from 'Verbose' ## 📣Feedback 📣 diff --git a/docs/content/patterns/avd/deploy/_index.md b/docs/content/patterns/avd/deploy/_index.md index 176e9f30d..2ea06592e 100644 --- a/docs/content/patterns/avd/deploy/_index.md +++ b/docs/content/patterns/avd/deploy/_index.md @@ -21,6 +21,7 @@ An AVD deployment with associated Insights configuration per the AVD Portal's AV ## Deploy via the Azure Portal UI +[![Deploy From LocalHost](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fhttp://localhost:1313%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) ## View / Enable Alerts after deployment From 7484794416c656ee8c0a8bffa723112f20043beb Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Sun, 4 Feb 2024 13:02:33 -0500 Subject: [PATCH 05/12] create test link button for deployment --- docs/content/patterns/avd/deploy/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/patterns/avd/deploy/_index.md b/docs/content/patterns/avd/deploy/_index.md index 2ea06592e..f9809ee6c 100644 --- a/docs/content/patterns/avd/deploy/_index.md +++ b/docs/content/patterns/avd/deploy/_index.md @@ -21,7 +21,7 @@ An AVD deployment with associated Insights configuration per the AVD Portal's AV ## Deploy via the Azure Portal UI -[![Deploy From LocalHost](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fhttp://localhost:1313%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) +[![Deploy From JCoreMS](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) ## View / Enable Alerts after deployment From 51a48b46da7fd4b7869d272232eb16efcd8fec04 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Mon, 5 Feb 2024 09:21:00 -0500 Subject: [PATCH 06/12] Update AVD deployment links in _index.md --- docs/content/patterns/avd/deploy/_index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/content/patterns/avd/deploy/_index.md b/docs/content/patterns/avd/deploy/_index.md index f9809ee6c..30aab43f2 100644 --- a/docs/content/patterns/avd/deploy/_index.md +++ b/docs/content/patterns/avd/deploy/_index.md @@ -21,8 +21,7 @@ An AVD deployment with associated Insights configuration per the AVD Portal's AV ## Deploy via the Azure Portal UI -[![Deploy From JCoreMS](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) -[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FJCoreMS%2Fazure-monitor-baseline-alerts%2FJCore-AVD-Initial%2Fpatterns%2Favd%2FavdCustomUi.json) +[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fblob%2F%2Fmain%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fblob%2F%2Fmain%2Fpatterns%2Favd%2FavdCustomUi.json) [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/CustomDeploymentBlade/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fblob%2F%2Fmain%2Fpatterns%2Favd%2FavdArm.json/uiFormDefinitionUri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2Fazure-monitor-baseline-alerts%2Fblob%2F%2Fmain%2Fpatterns%2Favd%2FavdCustomUi.json) ## View / Enable Alerts after deployment From 005e7c94a1fab897bf5bde8dd2efeac8087eff26 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Mon, 5 Feb 2024 09:36:46 -0500 Subject: [PATCH 07/12] Add PidcuaAvdPatternDeploymentName variable to deploy.bicep --- patterns/avd/avdArm.json | 233 ++++++++++++++++------------ patterns/avd/templates/deploy.bicep | 7 +- 2 files changed, 136 insertions(+), 104 deletions(-) diff --git a/patterns/avd/avdArm.json b/patterns/avd/avdArm.json index 90729ecfc..ee22e71c4 100644 --- a/patterns/avd/avdArm.json +++ b/patterns/avd/avdArm.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "8193403604703280884" + "version": "0.24.24.22086", + "templateHash": "2209245870913179541" } }, "parameters": { @@ -23,6 +23,13 @@ "description": "SaS token if needed for script location." } }, + "optoutTelemetry": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Telemetry Opt-Out" + } + }, "AlertNamePrefix": { "type": "string", "defaultValue": "AVD", @@ -153,7 +160,7 @@ "ResourceGroupCreate": "[if(equals(parameters('ResourceGroupStatus'), 'New'), true(), false())]", "RunbookNameGetStorage": "AvdStorageLogData", "RunbookNameGetHostPool": "AvdHostPoolLogData", - "RunbookScriptGetStorage": "[format('Get-StorAcctInfov2.ps1{0}', parameters('_ArtifactsLocationSasToken'))]", + "RunbookScriptGetStorage": "[format('Get-StorAcctInfo.ps1{0}', parameters('_ArtifactsLocationSasToken'))]", "RunbookScriptGetHostPool": "[format('Get-HostPoolInfo.ps1{0}', parameters('_ArtifactsLocationSasToken'))]", "StorAcctRGs": "[union(variables('StorAcctRGsAll'), createArray())]", "RoleAssignments": { @@ -170,6 +177,8 @@ "GUID": "92aaf0da-9dab-42b6-94a3-d43ce8d16293" } }, + "cuaid": "b8b4a533-1bb2-402f-bbd9-3055d00d885a", + "PidcuaAvdPatternDeploymentName": "[take(format('pid-{0}-{1}', variables('cuaid'), uniqueString(parameters('Location'), subscription().displayName, parameters('time'))), 64)]", "LogAlertsHostPool": [ { "name": "[format('{0}-HP-Cap-85Prcnt-xHostPoolNamex', parameters('AlertNamePrefix'))]", @@ -1715,6 +1724,32 @@ } }, "resources": [ + { + "condition": "[not(parameters('optoutTelemetry'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[variables('PidcuaAvdPatternDeploymentName')]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": {}, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.24.24.22086", + "templateHash": "2976306977123719457" + } + }, + "resources": [] + } + } + }, { "condition": "[variables('ResourceGroupCreate')]", "type": "Microsoft.Resources/deployments", @@ -1744,8 +1779,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "14479610109813008203" + "version": "0.24.24.22086", + "templateHash": "2517070614714634945" } }, "parameters": { @@ -1853,8 +1888,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "727668444186100245" + "version": "0.24.24.22086", + "templateHash": "5261637614282567226" } }, "parameters": { @@ -1983,8 +2018,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13976546302901379815" + "version": "0.24.24.22086", + "templateHash": "10735397712111716035" } }, "parameters": { @@ -2355,8 +2390,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13669706201001327225" + "version": "0.24.24.22086", + "templateHash": "10816872289601798175" } }, "parameters": { @@ -2743,8 +2778,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "9873337138567935156" + "version": "0.24.24.22086", + "templateHash": "10075722548658867517" } }, "parameters": { @@ -2897,8 +2932,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "12742772183632552384" + "version": "0.24.24.22086", + "templateHash": "1743310793956037703" } }, "parameters": { @@ -3090,8 +3125,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "11768955385651742227" + "version": "0.24.24.22086", + "templateHash": "5672032817815990833" } }, "parameters": { @@ -3292,8 +3327,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "5809431067482816549" + "version": "0.24.24.22086", + "templateHash": "4861308806440410181" } }, "parameters": { @@ -3441,8 +3476,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "15421173239527809626" + "version": "0.24.24.22086", + "templateHash": "14643058458584355557" } }, "parameters": { @@ -3576,8 +3611,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "8116463202302820849" + "version": "0.24.24.22086", + "templateHash": "10863078211007786001" } }, "parameters": { @@ -3715,8 +3750,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "10708379588686916495" + "version": "0.24.24.22086", + "templateHash": "17168218083015351722" } }, "parameters": { @@ -3898,8 +3933,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "3765098102914952048" + "version": "0.24.24.22086", + "templateHash": "1818160747538654546" } }, "parameters": { @@ -4369,8 +4404,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "5300610667995634254" + "version": "0.24.24.22086", + "templateHash": "2516207548282735604" } }, "parameters": { @@ -4566,8 +4601,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "4621144128017741284" + "version": "0.24.24.22086", + "templateHash": "4938350038598666744" } }, "parameters": { @@ -4701,8 +4736,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "7828421530828782575" + "version": "0.24.24.22086", + "templateHash": "14162095909703477931" } }, "parameters": { @@ -4915,8 +4950,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18129760119459600298" + "version": "0.24.24.22086", + "templateHash": "10103898822677715656" } }, "parameters": { @@ -5115,8 +5150,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "9545798095452579480" + "version": "0.24.24.22086", + "templateHash": "6953643909278292232" } }, "parameters": { @@ -5702,8 +5737,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "9545798095452579480" + "version": "0.24.24.22086", + "templateHash": "6953643909278292232" } }, "parameters": { @@ -6289,8 +6324,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "9545798095452579480" + "version": "0.24.24.22086", + "templateHash": "6953643909278292232" } }, "parameters": { @@ -6879,8 +6914,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "9545798095452579480" + "version": "0.24.24.22086", + "templateHash": "6953643909278292232" } }, "parameters": { @@ -7501,8 +7536,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "8842080406354769669" + "version": "0.24.24.22086", + "templateHash": "6843026320562123016" } }, "parameters": { @@ -7605,8 +7640,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "14548331241003575945" + "version": "0.24.24.22086", + "templateHash": "2992322436298442160" } }, "parameters": { @@ -7798,8 +7833,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "7497695042150647825" + "version": "0.24.24.22086", + "templateHash": "14047009403412009945" } }, "parameters": { @@ -8123,8 +8158,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18042637013033247" + "version": "0.24.24.22086", + "templateHash": "10294844695901575869" } }, "parameters": { @@ -8231,8 +8266,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "4325943649872987644" + "version": "0.24.24.22086", + "templateHash": "1702775665489531700" } }, "parameters": { @@ -8469,8 +8504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13865487144333348040" + "version": "0.24.24.22086", + "templateHash": "7596250978618982289" } }, "parameters": { @@ -8803,8 +8838,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "18042637013033247" + "version": "0.24.24.22086", + "templateHash": "10294844695901575869" } }, "parameters": { @@ -8911,8 +8946,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "4325943649872987644" + "version": "0.24.24.22086", + "templateHash": "1702775665489531700" } }, "parameters": { @@ -9149,8 +9184,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13865487144333348040" + "version": "0.24.24.22086", + "templateHash": "7596250978618982289" } }, "parameters": { @@ -9480,8 +9515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13090012199157395091" + "version": "0.24.24.22086", + "templateHash": "803368566998479649" } }, "parameters": { @@ -9581,8 +9616,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "4325943649872987644" + "version": "0.24.24.22086", + "templateHash": "1702775665489531700" } }, "parameters": { @@ -9819,8 +9854,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13865487144333348040" + "version": "0.24.24.22086", + "templateHash": "7596250978618982289" } }, "parameters": { @@ -10150,8 +10185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "12335470402732189346" + "version": "0.24.24.22086", + "templateHash": "9109246210899247112" } }, "parameters": { @@ -10251,8 +10286,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "4325943649872987644" + "version": "0.24.24.22086", + "templateHash": "1702775665489531700" } }, "parameters": { @@ -10489,8 +10524,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13865487144333348040" + "version": "0.24.24.22086", + "templateHash": "7596250978618982289" } }, "parameters": { @@ -10820,8 +10855,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "12574331042221135071" + "version": "0.24.24.22086", + "templateHash": "3526350418959145293" } }, "parameters": { @@ -10924,8 +10959,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "4325943649872987644" + "version": "0.24.24.22086", + "templateHash": "1702775665489531700" } }, "parameters": { @@ -11162,8 +11197,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13865487144333348040" + "version": "0.24.24.22086", + "templateHash": "7596250978618982289" } }, "parameters": { @@ -11512,8 +11547,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "87049037068146934" + "version": "0.24.24.22086", + "templateHash": "2422626877196222117" } }, "parameters": { @@ -11742,8 +11777,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "697362920563967240" + "version": "0.24.24.22086", + "templateHash": "3785960013952923339" } }, "parameters": { @@ -12069,8 +12104,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "6763740487781585899" + "version": "0.24.24.22086", + "templateHash": "3510317398781501346" } }, "parameters": { @@ -12179,8 +12214,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "87049037068146934" + "version": "0.24.24.22086", + "templateHash": "2422626877196222117" } }, "parameters": { @@ -12409,8 +12444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "697362920563967240" + "version": "0.24.24.22086", + "templateHash": "3785960013952923339" } }, "parameters": { @@ -12754,8 +12789,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "6763740487781585899" + "version": "0.24.24.22086", + "templateHash": "3510317398781501346" } }, "parameters": { @@ -12864,8 +12899,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "87049037068146934" + "version": "0.24.24.22086", + "templateHash": "2422626877196222117" } }, "parameters": { @@ -13094,8 +13129,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "697362920563967240" + "version": "0.24.24.22086", + "templateHash": "3785960013952923339" } }, "parameters": { @@ -13463,8 +13498,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "13713639001813517941" + "version": "0.24.24.22086", + "templateHash": "12268196190756010594" } }, "parameters": { @@ -13618,8 +13653,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.23.1.45101", - "templateHash": "8205837903548946509" + "version": "0.24.24.22086", + "templateHash": "10328615059869363586" } }, "parameters": { diff --git a/patterns/avd/templates/deploy.bicep b/patterns/avd/templates/deploy.bicep index daa3fd895..bccbc6156 100644 --- a/patterns/avd/templates/deploy.bicep +++ b/patterns/avd/templates/deploy.bicep @@ -104,6 +104,7 @@ var RoleAssignments = { // '92aaf0da-9dab-42b6-94a3-d43ce8d16293' // Log Analtyics Contributor - allows writing to workspace for Host Pool and Storage Logic Apps var cuaid = 'b8b4a533-1bb2-402f-bbd9-3055d00d885a' +var PidcuaAvdPatternDeploymentName = take('pid-${cuaid}-${uniqueString(Location, subscription().displayName, time)}', 64) var LogAlertsHostPool = [ {// Based on Runbook script Output to LAW @@ -1961,15 +1962,11 @@ var varTimeZones = { westus3: 'Mountain Standard Time' } -var deploymentNames = { - pidCuaDeploymentName: take('pid-${cuaid}-${uniqueString(Location, subscription().displayName, time)}', 64) -} - // =========== // // Deployments // // =========== // module deploymentNames_pidCuaDeployment 'modules/pid_cuaid.bicep' = if (!optoutTelemetry) { - name: deploymentNames.pidCuaDeploymentName + name: PidcuaAvdPatternDeploymentName params: {} } From 3475ca2cf7d24c724424059bf2ef0959b2a7060d Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Mon, 5 Feb 2024 10:08:06 -0500 Subject: [PATCH 08/12] Update telemetry tracking configuration --- docs/content/patterns/avd/Telemetry.md | 50 +++++------------- .../patterns/avd/media/AVDAlertsOptOut.png | Bin 0 -> 44136 bytes 2 files changed, 12 insertions(+), 38 deletions(-) create mode 100644 docs/content/patterns/avd/media/AVDAlertsOptOut.png diff --git a/docs/content/patterns/avd/Telemetry.md b/docs/content/patterns/avd/Telemetry.md index 91eba5083..9a77b92dd 100644 --- a/docs/content/patterns/avd/Telemetry.md +++ b/docs/content/patterns/avd/Telemetry.md @@ -14,51 +14,25 @@ To disable this tracking, we have included a parameter called `telemetryOptOut` If you are happy with leaving telemetry tracking enabled, no changes are required. -For example, in the alzArm.json file, you will see the following: +This deployment has a custom UI definition meaning you will then be provided a form to select options with the ability to check the box labeled ["Opt-Out of Telemetry"](./media/AVDAlertsOptOut.png). -```json -"telemetryOptOut": { - "type": "string", - "defaultValue": "No", - "allowedValues": [ - "Yes", - "No" - ], - "metadata": { - "description": "The customer usage identifier used for telemetry purposes. The default value of False enables telemetry. The value of True disables telemetry." - } -} -``` - -The default value is `No`, but can be changed to `Yes` in the parameter file. If set to `Yes` the deployment below will be ignored and therefore telemetry will not be tracked. +In the avdArm.json file, you will see the following: ```json -{ - "condition": "[equals(parameters('telemetryOptOut'), 'No')]", - "apiVersion": "2020-06-01", - "name": "[variables('deploymentNames').pidCuaDeploymentName]", - "location": "[deployment().location]", - "type": "Microsoft.Resources/deployments", - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "resources": [] - } - } -} + "optoutTelemetry": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Telemetry Opt-Out" + } + }, ``` + ## Module PID Value Mapping -The following are the unique ID's (also known as PIDs) used in the AMBA deployment +The following are the unique ID's (also known as PIDs) used in the Alerts Pattern deployment | Name | PID | | ------------------------------- | ------------------------------------ | -| Azure Monitor Baseline Alerts | d6b3b08c-5825-4b89-a62b-e3168d3d8fb0 | -| Connectivity Policy Initiative | 2d69aa07-8780-4697-a431-79882cb9f00e | -| Identity Policy Initiative | 8d257c20-97bf-4d14-acb3-38dd1436d13a | -| Management Policy Initiative | d87415c4-01ef-4667-af89-0b5adc14af1b | -| LandingZone Policy Initiative | 7bcfc615-be78-43da-b81d-98959a9465a5 | -| ServiceHealth Policy Initiative | 860d2afd-b71e-452f-9d3a-e56196cba570 | \ No newline at end of file +| AVD Baseline Alerts | b8b4a533-1bb2-402f-bbd9-3055d00d885a | \ No newline at end of file diff --git a/docs/content/patterns/avd/media/AVDAlertsOptOut.png b/docs/content/patterns/avd/media/AVDAlertsOptOut.png new file mode 100644 index 0000000000000000000000000000000000000000..b81ce808b5b54d37d25a3e464efa7935b69f8d69 GIT binary patch literal 44136 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 kJ8K$p{9~sp;vX_u#1T%arON}hfOvTFGRn_NpS Date: Mon, 5 Feb 2024 10:11:03 -0500 Subject: [PATCH 09/12] Update AVD Arm template and deploy script URLs --- patterns/avd/avdArm.json | 4 ++-- patterns/avd/templates/deploy.bicep | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/patterns/avd/avdArm.json b/patterns/avd/avdArm.json index ee22e71c4..2d2a06b99 100644 --- a/patterns/avd/avdArm.json +++ b/patterns/avd/avdArm.json @@ -5,13 +5,13 @@ "_generator": { "name": "bicep", "version": "0.24.24.22086", - "templateHash": "2209245870913179541" + "templateHash": "9773308776885557356" } }, "parameters": { "_ArtifactsLocation": { "type": "string", - "defaultValue": "https://raw.githubusercontent.com/Azure/avdaccelerator/main/workload/scripts/alerts/", + "defaultValue": "https://raw.githubusercontent.com/Azure/azure-monitor-baseline-alerts/main/patterns/alz/scripts/", "metadata": { "description": "Location of needed scripts to deploy solution." } diff --git a/patterns/avd/templates/deploy.bicep b/patterns/avd/templates/deploy.bicep index bccbc6156..b16c5c946 100644 --- a/patterns/avd/templates/deploy.bicep +++ b/patterns/avd/templates/deploy.bicep @@ -6,7 +6,8 @@ param SetEnabled bool = false */ @description('Location of needed scripts to deploy solution.') -param _ArtifactsLocation string = 'https://raw.githubusercontent.com/Azure/avdaccelerator/main/workload/scripts/alerts/' +param _ArtifactsLocation string = 'https://raw.githubusercontent.com/Azure/azure-monitor-baseline-alerts/main/patterns/alz/scripts/' +// https://raw.githubusercontent.com/Azure/avdaccelerator/main/workload/scripts/alerts/ OLD from AVD Accelerator Repo @description('SaS token if needed for script location.') @secure() From d75b925ef9cc4457284866a0eb55f9c0e85fecb3 Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Mon, 5 Feb 2024 10:18:27 -0500 Subject: [PATCH 10/12] Update telemetry tracking instructions in Telemetry.md --- docs/content/patterns/avd/Telemetry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/patterns/avd/Telemetry.md b/docs/content/patterns/avd/Telemetry.md index 9a77b92dd..749c2ea4e 100644 --- a/docs/content/patterns/avd/Telemetry.md +++ b/docs/content/patterns/avd/Telemetry.md @@ -14,7 +14,7 @@ To disable this tracking, we have included a parameter called `telemetryOptOut` If you are happy with leaving telemetry tracking enabled, no changes are required. -This deployment has a custom UI definition meaning you will then be provided a form to select options with the ability to check the box labeled ["Opt-Out of Telemetry"](./media/AVDAlertsOptOut.png). +**Check the box labeled ["Opt-Out of Telemetry"](../media/AVDAlertsOptOut.png)** when using the "Deploy to Azure" buttons with a custom User Interface. There is no parameters file for this deployment. In the avdArm.json file, you will see the following: From 5988b669a8f388c2f3ced24131e41876b82ad9ab Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Mon, 5 Feb 2024 15:37:19 -0500 Subject: [PATCH 11/12] Move media files and fix image links --- docs/content/patterns/specialized/_index.md | 5 +++++ .../patterns/{ => specialized}/avd/FAQ.md | 0 .../{ => specialized}/avd/Known-Issues.md | 0 .../{ => specialized}/avd/Telemetry.md | 17 +++++++++-------- .../specialized/avd/Update-to-new-Release.md | 9 +++++++++ .../{ => specialized}/avd/Whats-New.md | 0 .../patterns/{ => specialized}/avd/_index.md | 8 ++++---- .../avd/deploy/Avd-Deploy.md} | 8 +++++--- .../img/Avd}/AVDAlertsOptOut.png | Bin .../avd/media => static/img/Avd}/alerts.xlsx | Bin .../media => static/img/Avd}/avdAlertRules.jpg | Bin .../img/Avd}/avdAlertRulesChange.jpg | Bin .../img/Avd}/avdAlertRulesChangeLogbased.jpg | Bin .../Avd}/avdAlertRulesChangeMetricbased.jpg | Bin .../img/Avd}/avdAlertRulesCopy1.jpg | Bin .../img/Avd}/avdAlertRulesCopy2.jpg | Bin .../img/Avd}/avdAlertRulesCopy3.jpg | Bin .../img/Avd}/avdAlertRulesEnable.jpg | Bin .../img/Avd}/avdAlertRulesFilter.jpg | Bin .../img/Avd}/avdAlertRulesProperties.jpg | Bin .../img/Avd}/avdAlertRulesProperties2.jpg | Bin 21 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 docs/content/patterns/specialized/_index.md rename docs/content/patterns/{ => specialized}/avd/FAQ.md (100%) rename docs/content/patterns/{ => specialized}/avd/Known-Issues.md (100%) rename docs/content/patterns/{ => specialized}/avd/Telemetry.md (82%) create mode 100644 docs/content/patterns/specialized/avd/Update-to-new-Release.md rename docs/content/patterns/{ => specialized}/avd/Whats-New.md (100%) rename docs/content/patterns/{ => specialized}/avd/_index.md (95%) rename docs/content/patterns/{avd/deploy/_index.md => specialized/avd/deploy/Avd-Deploy.md} (94%) rename docs/{content/patterns/avd/media => static/img/Avd}/AVDAlertsOptOut.png (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/alerts.xlsx (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRules.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesChange.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesChangeLogbased.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesChangeMetricbased.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesCopy1.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesCopy2.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesCopy3.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesEnable.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesFilter.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesProperties.jpg (100%) rename docs/{content/patterns/avd/media => static/img/Avd}/avdAlertRulesProperties2.jpg (100%) diff --git a/docs/content/patterns/specialized/_index.md b/docs/content/patterns/specialized/_index.md new file mode 100644 index 000000000..0bcd11035 --- /dev/null +++ b/docs/content/patterns/specialized/_index.md @@ -0,0 +1,5 @@ +--- +title: Specialized Workloads +geekdocCollapseSection: true +--- + diff --git a/docs/content/patterns/avd/FAQ.md b/docs/content/patterns/specialized/avd/FAQ.md similarity index 100% rename from docs/content/patterns/avd/FAQ.md rename to docs/content/patterns/specialized/avd/FAQ.md diff --git a/docs/content/patterns/avd/Known-Issues.md b/docs/content/patterns/specialized/avd/Known-Issues.md similarity index 100% rename from docs/content/patterns/avd/Known-Issues.md rename to docs/content/patterns/specialized/avd/Known-Issues.md diff --git a/docs/content/patterns/avd/Telemetry.md b/docs/content/patterns/specialized/avd/Telemetry.md similarity index 82% rename from docs/content/patterns/avd/Telemetry.md rename to docs/content/patterns/specialized/avd/Telemetry.md index 749c2ea4e..2f5faf16c 100644 --- a/docs/content/patterns/avd/Telemetry.md +++ b/docs/content/patterns/specialized/avd/Telemetry.md @@ -14,18 +14,19 @@ To disable this tracking, we have included a parameter called `telemetryOptOut` If you are happy with leaving telemetry tracking enabled, no changes are required. -**Check the box labeled ["Opt-Out of Telemetry"](../media/AVDAlertsOptOut.png)** when using the "Deploy to Azure" buttons with a custom User Interface. There is no parameters file for this deployment. +**Check the box labeled "Opt-Out of Telemetry"** when using the "Deploy to Azure" buttons with a custom User Interface. There is no parameters file for this deployment. + In the avdArm.json file, you will see the following: ```json - "optoutTelemetry": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Telemetry Opt-Out" - } - }, +"optoutTelemetry": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Telemetry Opt-Out" + } +}, ``` diff --git a/docs/content/patterns/specialized/avd/Update-to-new-Release.md b/docs/content/patterns/specialized/avd/Update-to-new-Release.md new file mode 100644 index 000000000..c31a619d9 --- /dev/null +++ b/docs/content/patterns/specialized/avd/Update-to-new-Release.md @@ -0,0 +1,9 @@ +--- +title: Update to a new release +geekdocCollapseSection: true +weight: 70 +--- + +# How to update to a new release + +Currently, redeploying the solution will update the existing components to include the alerts. Stay tuned for additional details as new releases are made available. \ No newline at end of file diff --git a/docs/content/patterns/avd/Whats-New.md b/docs/content/patterns/specialized/avd/Whats-New.md similarity index 100% rename from docs/content/patterns/avd/Whats-New.md rename to docs/content/patterns/specialized/avd/Whats-New.md diff --git a/docs/content/patterns/avd/_index.md b/docs/content/patterns/specialized/avd/_index.md similarity index 95% rename from docs/content/patterns/avd/_index.md rename to docs/content/patterns/specialized/avd/_index.md index 718346778..b50c1c1cb 100644 --- a/docs/content/patterns/avd/_index.md +++ b/docs/content/patterns/specialized/avd/_index.md @@ -56,11 +56,11 @@ If you have encountered a problem please file an issue in our GitHub repo [GitHu ## Deployment Guide -We have a [Deployment Guide](../avd/deploy/Introduction-to-deploying-the-AVD-Pattern) available for guidance on how to consume the contents of this repo. +We have a [Deployment Guide](./deploy/Avd-Deploy) available for guidance on how to consume the contents of this repo. ## Known Issues -Please see the [Known Issues](../avd/Known-Issues). +Please see the [Known Issues](Known-Issues). ## Frequently Asked Questions @@ -83,14 +83,14 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft. contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. {{< hint type=note >}} -Details on contributing to this repo can be found [here](../../contributing/patterns) +Details on contributing to this repo can be found [here](../../../contributing/) {{< /hint >}} ## Telemetry When you deploy the IP located in this repo, Microsoft can identify the installation of said IP with the deployed Azure resources. Microsoft can correlate these resources used to support the software. Microsoft collects this information to provide the best experiences with their products and to operate their business. The telemetry is collected through customer usage attribution. The data is collected and governed by [Microsoft's privacy policies](https://www.microsoft.com/trustcenter). -If you don't wish to send usage data to Microsoft, or need to understand more about its' use details can be found [here](../alz/Telemetry). +If you don't wish to send usage data to Microsoft, or need to understand more about its' use details can be found [here](./Telemetry). ## Trademarks diff --git a/docs/content/patterns/avd/deploy/_index.md b/docs/content/patterns/specialized/avd/deploy/Avd-Deploy.md similarity index 94% rename from docs/content/patterns/avd/deploy/_index.md rename to docs/content/patterns/specialized/avd/deploy/Avd-Deploy.md index 30aab43f2..795a3f270 100644 --- a/docs/content/patterns/avd/deploy/_index.md +++ b/docs/content/patterns/specialized/avd/deploy/Avd-Deploy.md @@ -31,8 +31,10 @@ You can also review the Alert Action Group and adjust as needed with additional 1. Open the [Alerts Azure Portal Page](https://portal.azure.com/#blade/Microsoft_Azure_Monitoring/AzureMonitoringBrowseBlade/alertsV2) 2. Click on the "Alert rules" section at the top of the page. -[Screenshot](../media/avdAlertRules.jpg) + + 3. Initially the list of alert rules may be filtered out or appear missing. Simply change the filter to include "disabled" or click the "Clear filters" option. -[Screenshot](../media/avdAlertRulesFilter.jpg) + + 4. Select the check box next to each you would like to enable and click "Enable" at the top of the page. -[Screenshot](../media/avdAlertRulesEnable.jpg) \ No newline at end of file + \ No newline at end of file diff --git a/docs/content/patterns/avd/media/AVDAlertsOptOut.png b/docs/static/img/Avd/AVDAlertsOptOut.png similarity index 100% rename from docs/content/patterns/avd/media/AVDAlertsOptOut.png rename to docs/static/img/Avd/AVDAlertsOptOut.png diff --git a/docs/content/patterns/avd/media/alerts.xlsx b/docs/static/img/Avd/alerts.xlsx similarity index 100% rename from docs/content/patterns/avd/media/alerts.xlsx rename to docs/static/img/Avd/alerts.xlsx diff --git a/docs/content/patterns/avd/media/avdAlertRules.jpg b/docs/static/img/Avd/avdAlertRules.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRules.jpg rename to docs/static/img/Avd/avdAlertRules.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesChange.jpg b/docs/static/img/Avd/avdAlertRulesChange.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesChange.jpg rename to docs/static/img/Avd/avdAlertRulesChange.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesChangeLogbased.jpg b/docs/static/img/Avd/avdAlertRulesChangeLogbased.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesChangeLogbased.jpg rename to docs/static/img/Avd/avdAlertRulesChangeLogbased.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesChangeMetricbased.jpg b/docs/static/img/Avd/avdAlertRulesChangeMetricbased.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesChangeMetricbased.jpg rename to docs/static/img/Avd/avdAlertRulesChangeMetricbased.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesCopy1.jpg b/docs/static/img/Avd/avdAlertRulesCopy1.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesCopy1.jpg rename to docs/static/img/Avd/avdAlertRulesCopy1.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesCopy2.jpg b/docs/static/img/Avd/avdAlertRulesCopy2.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesCopy2.jpg rename to docs/static/img/Avd/avdAlertRulesCopy2.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesCopy3.jpg b/docs/static/img/Avd/avdAlertRulesCopy3.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesCopy3.jpg rename to docs/static/img/Avd/avdAlertRulesCopy3.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesEnable.jpg b/docs/static/img/Avd/avdAlertRulesEnable.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesEnable.jpg rename to docs/static/img/Avd/avdAlertRulesEnable.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesFilter.jpg b/docs/static/img/Avd/avdAlertRulesFilter.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesFilter.jpg rename to docs/static/img/Avd/avdAlertRulesFilter.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesProperties.jpg b/docs/static/img/Avd/avdAlertRulesProperties.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesProperties.jpg rename to docs/static/img/Avd/avdAlertRulesProperties.jpg diff --git a/docs/content/patterns/avd/media/avdAlertRulesProperties2.jpg b/docs/static/img/Avd/avdAlertRulesProperties2.jpg similarity index 100% rename from docs/content/patterns/avd/media/avdAlertRulesProperties2.jpg rename to docs/static/img/Avd/avdAlertRulesProperties2.jpg From 026daa27eb5358d462a8fa9c7c09eee039a4271c Mon Sep 17 00:00:00 2001 From: "Jonathan.Core" Date: Mon, 5 Feb 2024 16:01:54 -0500 Subject: [PATCH 12/12] Remove sample-pipeline.yml and sample-workflow.yml --- patterns/avd/examples/sample-pipeline.yml | 17 ----------- patterns/avd/examples/sample-workflow.yml | 37 ----------------------- 2 files changed, 54 deletions(-) delete mode 100644 patterns/avd/examples/sample-pipeline.yml delete mode 100644 patterns/avd/examples/sample-workflow.yml diff --git a/patterns/avd/examples/sample-pipeline.yml b/patterns/avd/examples/sample-pipeline.yml deleted file mode 100644 index 1906e8b65..000000000 --- a/patterns/avd/examples/sample-pipeline.yml +++ /dev/null @@ -1,17 +0,0 @@ -variables: - location: "northeurope" - ManagementGroupPrefix: "contoso" - serviceConnectionName: "AMBA-Service-Connection" - -pool: - vmImage: ubuntu-latest - -steps: - - task: AzureCLI@2 - displayName: "Deploy AMBA ARM template" - inputs: - azureSubscription: ${{ variables['serviceConnectionName'] }} - scriptType: bash - scriptLocation: inlineScript - inlineScript: | - az deployment mg create --template-uri https://raw.githubusercontent.com/Azure/azure-monitor-baseline-alerts/main/patterns/alz/alzArm.json --location $(location) --management-group-id $(ManagementGroupPrefix) --parameters ./patterns/alz/alzArm.param.json diff --git a/patterns/avd/examples/sample-workflow.yml b/patterns/avd/examples/sample-workflow.yml deleted file mode 100644 index b70b44fae..000000000 --- a/patterns/avd/examples/sample-workflow.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Deploy AMBA - -on: - workflow_dispatch: {} - -permissions: - id-token: write - contents: read - -env: - Location: "norwayeast" - ManagementGroupPrefix: "alz" - -jobs: - deploy_job: - runs-on: ubuntu-latest - environment: deploy - - steps: - - name: Checkout Repo - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: "Az CLI login" - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - enable-AzPSSession: true - - - name: Az CLI Deploy AMBA ARM template - id: deploy_amba - shell: bash - run: | - az deployment mg create --template-uri https://raw.githubusercontent.com/Azure/azure-monitor-baseline-alerts/main/patterns/alz/alzArm.json --location ${{ env.Location }} --management-group-id ${{ env.ManagementGroupPrefix }} --parameters ./patterns/alz/alzArm.param.json

5J$VxvFF{kA`X0pB4=;v(z_L~DhFPmvIT?j*2E1^R6ObA*JHFMV^!7zu zN3Fz*%$GgJBD)^46vp|PH7B7__vhcmHKI$vfP|h1_LYi z0qJmtfkMZGyyREu6{~bXT(QF{oH4U*T8ICDxE>V3>ld-nIV5BRMRrN`O!{?AIc9J* z)~rO^`Ibk-_Xo7G%6}^cP2&+#je6i(9vntx92Zig@a~hERp#<;AS(7grfr?_SPBV;e zCx%LRZS?Ayq#b$ySXS6TnYT3SOEUC38Tkar4p9?YTvfO|yUDS6{6f<&;O?4Ki`L9C z?^PuGbVK@4B6=`qRPA1&1Nua0wJhMRMyVWQNXi0%5iN7>Y;Gp*`CasufeuNh3CpH- zH+vcm*dNA|%!gs){nCaH60hyIV7*j&+*lX=fFK6R{szxLTtMH`3&fva1M(aO+sjeU zGqmMfmovBNdm7koHMqYV&c1H^RC|8cLfWJ;0QP+-qV1uP;HwGA7sERnMb0HoyS`)I z^(u{!Md(o&3g!(U59pgx<`FuwN$w@iiT9d>O6!#Xe&wd*0HQaB)!>WyJS>eH9@>|Y zLnm0o^*XxfT+p{Ml?h_{J10EjBfK#tkZ1Mu;YPjsV2~jiXuqWH!ukQ@0E?Mqt!im! zT;C;+{37+^dq@B$lJtPMa275~^_aLy)a*O~+89=T4G&np%ZC-@>g0j9$25|Ce=o;u zgBHQo?(O~;CfZp)9-~;L8hDS?@9+b1VylkLYK*&Q{grBp&Fa0PZZ%n8|8v*rB!L7z zI$1~E{%s}OeF_9TJx-@WYbS)ZA~XDwwfbd-VU*&8O~5>nulISxT^ljtx}zF35ITQ} z6#n6`58338`6W}S6xzO;UfK!%@@8$-B>KX7alio~!CKL}su=MC2tYvy6vAC`)P(#Q znO{Z1-;10KvMsxL2X@9dS6*~4+XTaR)KcoiJ?AYa&^o)T?)b*cZ{J;9s%LuF=9{5< z6m7CwmJ$)#@^^(~8jesE6b7R;tlpk>B{V<2o^2`A6sRe`&*cwp*N;f^jg^C(H+%WO z`*NLdl2B-`<+Z|w$jcmGY?2obn~#1p*xh`TntjGgth%blWpt98hxCJx#f(XeF9#{G z5(MDx{qD$`?@$K>+-ny(ivyOfCq7RgEw?)-YWUQv@H#O8KzfRbBH-;L0;HTE?vb8{ za*U^wDcYB5umj9|u)cn`skM84W$|)^hk;_xt1wkbJ+;-zZzCU2WOCS$w*0gHA=%J?erG9nvG42{mxY2EsX5<`^ zmCp0*WK>{IbI$Y>`NlLgAmB`9eq33!LEx)x@>H$8m>3`JH{~)pqHAme1P6&$L4JbC z6&10vPU|;H*~IJLDN8Zjy$wp#Tk4`!5|aE(@+o0y#5Xz_qD{4AUEkKm*n)1ECVV+Z zBch~UxfE{jnd^yH_QVSI>)nnX_Vk?Lq~wExIX?w|Y;z-Pt2sJK%^lw@hpm1m46Z`G zs2JY)jF^&oV`}|?p%A`oFXaAGSnTXagUu52U4!96GiMpEqBimBw>`J2XNhlK(VkP~ z(6vK|kTU>pAL4<#r*!0Io$+D4*}`N!Z2yf*je7P%xd=uh@6(}d2x=-VI!kz1?b&p0 z3l}=hAnST{)teyVq)xReo!Gt2T)nY-c&`t00WCH|K>18*~ey@|7y+h_^4tr!RNiWcPmE7?}v)ILOCHS5-F>V*{|9K*DFx zRZ>YPUcjP}Wltpux^)l)f8U zA<5=^{u>(ltq#2h1H2eH4B4kn#T>5rY{L97m*pf3t@b(@r2HgS_VaSQhaYe8%2T}x z4}GtZ-!poRDs#xMmF{dW?Re#hl_Rc!{uZWHD1Axc&L#aw9xM};iz1)&u&OHDx?!lL zv_wu)D*j=;M?%)xg`INEK}p)2Q}uGUCyN%h*0g z|9?QS>Zc;5br!ZdO_on?p5+swNC78$3%kfgo zO#Uov|Hj_%y_x$j_VPh)^bf2Pa0{QT8OMY89kSR+9qGkf!}f_50%z>_eV=-6yVqA~ z3y#-SY*30M(<7dvl}?HXavA~@9 z?V@)(+|Hk;1svh;`<|!W=6Ud@D*+}9Q)m7Cqk0O=D_tAL6Fa3ohPftVWY1e-ZZE^) zp7$wFSygmm)!%qEDVHKa<}8Jh^{B2qGvu!+En6?y&awm^8??TTD0l1hWPvXWD_!_k zlF6+T))oOQ_l9}ahacp$6Ux+#Fjvd^&w3_c?q;OF3a5my2^1M5jHuqn;3z*np0bYmo*mY zX4M~R71>zs3w0=OWtd*xFsgdeIPJSr?=xaO@i!}1?TN6X!SKzSl1|y{Cyg%k)1BWb zhPUA1IIhFaNI(G#W{3B*tO$4PKWY4wmqKS}5;JD(Ya>cjrQJg5j3Hidqv){qo_Q9V z{4VJ@Cn48u^hI86crC;7VaFt%cVq7~gHpRBxf?F|<1Dqs+`94lX;0~NSgqQtj_m(& zbF9Vci*U_5p@ij_b*LQr5(5TMf-F<>k5-I$$5fJ!877D`>tgp&vBc|T+`GK`0P|@t zLK)s`xI;r;R#|dNa;|=B{B&b0H_nQ<))B`Eg$L-=W(D|(rH$Vdb-k+b@|)cL zu&^j7cHw}FzWezo*<0z#sFAUTp0wQd(49yC&0F{O4ERwVd7Zu)EuB0*!!?y`5dvO1 zDY7~8oO==d=24&9>&OF9gTFCnOMfRuUhNmW&hO9Eb5I6?9AHO7Qz*!%?)C8kzLwjO zaeBsO1?7rSRSQ`+y(LS(*xAcRJ=Ipr(!Gv2b3BxU1nJH=)o1ky-jiS$UCa*LF6P=; zs2J!7C9{%f8lIiV9-YX49*5ZW8K3Q3y_^9<@Ep_ON^9_v@n!p}dcg!!>8c@8gpl`L6Q3d6GkZIV1rXn<{L-Y z?2;L1HUMK%1NEj%ImSYM1cB87HF=Xry=&u;@4ip zs&2@Zx>~tcs`Xzc@PcJjS=^gRY2viQK@x~J0ORN@4iXN0rAlp+D@ zyEaqq6FC3XrO_#y=t{WsV-g`0g+Imx* z+Sb3FYpgIk`z{CNp$HAB3E^F7+KaYSE_|+5(TorovqLNNOir6~8Hh@TE>^S52RG zF$wT3ZQ>2oPlxIweH*%ud6r*{jXDeidR|0fm8DE?G(3$29qI$rB_vKCz62%l9LXMG zk4n?M^Ul%h2DVeD^6B^DPfTc5l!-Y_2tI_1F~M^_He1+@<8mcm^Y+S8;bKG{~+4eyA(V=Q zGxnLSNtjx-3}MH<(~)IED2F$xGZJCw_mmqG5a-6~*u!1>#xJi5677ve&(U;1LOef2 z?yr)Q@QUQ1tV=4fh+C;h7Ypz;^&1_EyEE84*yK^^+xPc&CXRlOR-)JtGGmpEEUFF^ z&YaSCQlE#N?!$ZEE#uh{4If5Un>)pL??FP5&a4*!wNw?%6o9yvcmQKcA8TDZ`SdA+U=W|u04UlM z#Q}V& z|K;m4i8A9diT`Oa|F@d@U#e;e>IWO)+|rG4PZxGe%T7*$lB(HzrQ_zG$#b7Re>O~7 zHz&q@^d1o2ihnmRG+bJhX@IPCa^Umn^if*Rt{0@4ofzdo5p|&v<5XJy2LITv?)clO zNY?B{!gi6eDidm(h$W<*<^h79j|c%G@{un1(IBCcw@cW26Vb8qekC^n<4n}!B>)`s zvCU`Amn+x`>9p@%2vob1tZR;0Ta#D%j!(;Cn8o;`w`TVDA@qQGJ=3E3q`g0>WCLw( zlIt>KS5+U;c@6cQusb+z7t|0mm}c6#8>J$-p;8*{g0K1 zaJ4nwvM&EU_b)dW|5{92Ah5WB{2tFpvmY6+Ne?FoC4{o>6R;UzvF?%I#cq2;&$uOr z5bmVbHt=~sY4*Vs!@)f+d<}!H3zaW`*W_)BjL-6tlBKCKaSgW+LA?R^ ze^d{p#ad3Do%CmeqjY|;B(sun?+Su@H_L+s*%8i z-Q{0d^R`JViONd`y{)$-qXpxh?(2V3UWn%5S*T3i{>RTUfd39>f?eAmDIQryQ|`a^%SwN7c! z1V#WFjFBYU;8;UVFl>X~*qh^8hhJrW#u~c~h?a?3{1lbQ=X?GQFq)wv7X-05dxRp{ z4hPxR-(Da;+_wLHYCla`sP>EDo2z*97ru95SCRGOy*l=+2$1q|BDnR#g++)$rAfF< zWQX}Pm3b^+A0RfM_av?ryO`;5rgZU&-P80Utu><<5KsRB1&6>@dj0`Dt@@}vcEE^a z+w@`oIa&IXy{nB)_4ebrO&Z{w;qKBdffneD^y(0~P)DpsVw7ujDjTZQsiU|m8`s_2 zE2RVV$3egX=!>T(c$biqmRv)qH-LHz%Ir8VGy?Nl|CYE$(7qL-^k(f1qO|mZrJIW2 z9!R}+NDWy4MQDy53`8J#gP1nK7fTyXZz2Sokh}_RFuPAShR%8Z@wKyOGlMj(2ztQs zhrI-(sS2jH&Wj~%Q+T{m=K1Sll`<@v-a2!sYPmJ7N!ORaO{z}y5jV!F>UTMit*Q=S zVzSk4@{SC!zm)$_^UC+BbHlF?4(*u?`>ciuky8kDsCt1Op}Zu^xJBiN>zHRJJ!5YV z$gTc%BvTxM8#mGrDa(5utolAs_Ec$l(-1*VO6{;C0giZp5OrmOzXw$oSycJMI{;q9RHi*?q76y|KgO3P6@L3Q+8`Mqh~}J9r_~f^f2=pG zjnir===Zc%Bpx6A7*^wSSYlN zJ$n%*cCca z#sQM^e%3_Q=w`JCH6v;&9EmVndx>1zdY|!5>(1`Yyos= z7Nmw?A!ifQ@UD<{v@e91^3Ji1*4HCr_Tk6(e4lM;COrN5|C-?EEzFjK;q!diZ{h?a zjgvn#pBD}ROf)CPr;me1vhI+rPR-zumMs3np3X-NaXMBebQO^rqYe1-Unv>}O%kuf z4Q_e(^_h^soqevjJOb8+7}w&XVjW7{c62j?zVjJp`)k_g6(#k%t6kCM&n(F89L-4N zU(O{l!d2j|ab5ZuMG~O0FKm}>550j@1kJ4Zq29hij{7;qOj0*G8&%s;m8q2L;Hq2X zZeSAIohr|t-ULr#T^6W6l}79{S7I^No)ystO5a#>gXk#&#Gk1EFcdj9KJW+~)im#X zA|-qUmS&YRtok|S4OewefYKGAZwHGuP)pKX!2L$wW+ku$>2M9jsQSqr*AbWAp@-yG zVm9d{M9%SI44axnvos$;@4^F)>c-r3 zFyLm?8^*o{hX+Yikt_$dPJY(<{ilLo;btX>-47(JGH#Mi>a--Yo}Q0r7z(&eqkycpANwGo=dCo;h>D|=cQeRQA{xfT0=Q;QUllSsuL!=u4HK|cc^=h zs;g=HodWGh975%hxo-B>K8bru1&VbY_KaBU)Osorn!#50psyUb&<~OO~_?+ z#!2`>`-#^jm+5W(?GxYWG4(YR)4SfQhuv!#uLPgdd!Alcw`qrRQm-N$NlYi|QQUDt z0KYvg`p1NR_>20tA~I8xk-V|X^g>RUAu7upeCEi6oVp0Wh$FHt0*RtJPTdHz98H|` zJE-nuRFJowcIzk=9_7h>78tECog{Gk6PwR z99Q|bwK_j%uc|#(;_9XLF9?(&?Xi(*uF)_ljUqwz-V5UyBYxL5`7Vb=W5#Ft$NNWm zfbGUEAkFMirrtnK;_Lu5322GYzL+ ze+Olin`6};61g=)c6wI#E_t+B)FLM{A*u`UCsi`3lGT6zI%#YS)%JZ!mDgncNE&)- zebH8ROqA2FFozV+LhpR~oE~U&0hFS5=TK<=cgV}~w8g*)Jf?B&XR3ULN2hDLvt+W- z!WC1uc5N4Wz0rl~B2#iN-Ze~Jd~}=Qjspn6fR)I#bSNc|MIi9xX>kuR`^S%Nc3xv; zsoTfDbp4=lSVw3$l8q`dL!K}ogY6ZHJVn&2lgMFyZ$>|a_s|&jo;!EZgy~yG+31K2 zU_l<$V5#27T-5K3jWa4G7w z#+`y)GN9;_+fL}4@2M_YXWHaRW8w;`j`GSJ_*)ic9QWd%ewO5=F?Cz-I|dQ%gSD&> z0;qOmxi4@w<+-$Qf%1SP8(L;dQus2@KGy$T%ALd9J050oAxbxziKBJ6VOO`J{wy2v z>J+9j%qP#vjZhpn;C-Wuv8^^`t};UYR{Q{DNEWq1-Y&$MA}i2sAtW^w^Q~%wXGA-<)kBloj{@a-*Px-^>1Gy|et7AoV0CtU^6IzeC|M1%wS?0aT9xQ!liG@L|bcme<) zJ~pF{t~+2)E+6jawPA&!l;`9Rcvfylg!SfeqE(&C+)5Zkg7!mvQ7FC8g+TC9+zcbm zzFm?aVn|AD0u#>cbw|F9*fhBE&Nh_@nn^IZw-iNJ2z&T-=z{c#e5eO!q3SA*{J$&x zHL^V&>|d{*b27`EmGD@bLn z1i$2}dvv6R-}-VzT9{#y>>W7LCth5}JJ?EMeTaF%ccU`^TnQ=l=;shs4(_B; zGa3ZG9@iElGd$ie%R9Ywm!_JY>0;FsT5NT>T*N0(f;Addx%!DT(!5AU;o85FKc^69 zWMiDGl&{Q}*Ch^{pPv%BLuZ+?^!S$4@ z407=EDXuU<3ZRR;VG#fE{{QTu3W-MU8F-f+X9K1aZA&{JBv70!15C9>hf+*L~REi~yI(A1>2vPLDbyeYFp9M*WYl#$Xp)j1da?FlB!_i<1=J3hC z=z!-Uu|LU3qw$iRwUL9F-gT*wKedU|^a-!5_0}XcYw*5`)yywgHLdDdNiKsiorwR)kN-D`sS5D`chA1X)1tk2Bh}m% z&3@^JPJSfMqgi`OWKT{`PHE(4J01r~fFE@;5pa#aAd9sbDr9{6<`_GjnEKneB0fEr zvE~Z~nnBZVs^+O(iN5)y*Y?ej!Mxc(-kdJX`+Um8t#a+FUl~+xDP8@r%)ag@p*VKw zXXY1xAkYSQkN+>J1Qx^I0dw>Q=ux??g5omR48oL6+4Rx2=HJyuj-{ioTK&68h?B;3x`iZ@YTLGyZV(92yI7}5xJMa;$txG$?TSR@b&GLKMkl5A! zbuuGSdpYUF&=ZI1i0D46J<^m5d|LC4F;PJAJG8u#R~q3Hkm8UJ6uKPZ&!$ z|G&K{llag3@jqTmLR-Ll3hz*dXY|x)^R5Ev9ns2~NVsLE+%q8||03FW%_U==w_iEq zX=AJ_WzB!H;-nsZ}+|_rLDQ8s3BTZTSZMZmR2uRT_|cUX=zOowpLI;o`5 zDjtv{>a=lWW6G!%LuOxhM^=B+4a|2PsE~f!bFU$M^>R%cee~Zip2r3pdo^6^dYP;cyCE zEQT2^Aq8CZ&9%f+t#g^j3PKA1cqP>*W{>S=@!sjlpbulKsaOUts*IafUFLY^O7g{$ zKmEITSa!WZH)ksoTSBfe^<6_b(~{}zYe-SL(mI`+bb5l8o8=Ah)-e3i8*|HbxGL0M z*y#=wb)9q(DVmJuFz&wS95uON%~K#&Y4Q3~HfIAbBN6}!FapH5^T3b?0d(L=G1@pO z;5W#pAb!$QJ z)~0X&=-We)u>MdsCqXgB%<_MSz3^=!7!fWRv7gT=|_sIWjpFs&(&k>mgZ! z0>&l9VTH86Y1(~3K-{#atpc=NPX72tCvo$7j8xUL)_FUe{>L?MVEEC@hQzGz2o6mH z*{aM8>eWoA4TyVsw|3RFU1DUt$nT?^SMrGmfAeu1CJJHv!(AAYnip)(}W`_sdO_)6(xF!!xkLXWE9o-TDI2Sf<9w4@W-x4 z&z!&`HK(8O&kd+3IzO0?QnIjs$S?^N%If1{bTl?L!7!0Z7Bom2(B>}wrlk3>tmi!Y z)8~m1A`Pn>M&~CafSr2`s_jjH*YVUbHU6K=xBs8Ywqxu=+Bd4Q2gw9C72~NyfNY71 z5=Tbm9IZdQIr#s1F-d{J{4(7624Wg@7RcPh$$_9Kt430Ef=Reo%6%W^!9t_2g7d#6 zzKDxeF#a?OJW#)QRzZGE;KI>VzOZ5G+sKD~8bEhMlnS710wmtO^~K`hH~G2^^OA=i zO$bE-Vx|W;V>q1f-;kXkzKpiR%`Y?EO^#6am8~=FITfO}+4eZ+O9A%;j5Dqqdrs49 zi9;NGVze5$VlJCX)>N7-NZJow`Sa^Jk=Qgk(_k8&H_izCe*3T0#?=26-E{ z%EjS(4{Cx7`-^+MZi=*U2X1hQY!3a!;Di;{1vnieu9^LW5tspQrf8m*YCC=J%^cSW z)FD{Dyfvjbu2`=??Me11HbAQXAOsAN3-%~q!EG4lp5k*e$x73${nF=Nhn~XP-!Zns zxwLtc*xGuMDW24AC$ogEa_>w+ZiL?HMFO6;lV~gr{7HJ7m+O9?;pBT4Goz z)`YkaJEf@mRV&Zae@5@k*B4L!VrcdE_;i&YW%i28=0w+3o<5|_?2Cx>KV*|FE_biL z)LnGtnbfF24t?8TbqsJ?wBd{9^JM*QR*t+O!stpZE zfN?C^gSzSbBtJ}|rubK556(P}tQiC5#S4{qXUE2KffB;x-_G>t6ZN%BuLxfgZkR45 z60*oQWj-`^6HzTWE_*e)rXu;vj=UuRG1*XeZ`LJF4*H3MB%4e3bmiu@xH;2xc zLBo;f<@!G!J@?f_U1K>j^iyM>42lB`U7XE0&?yS44Ro$WillhAapY+|ByOIgR(jJQ z^>W^gL;j1Ym6baAEr8FN!)MPt5udTDUs=0%%bPu!jFcLcnXQ{xkjA@fR@S*{LC3@=>- zn!aQ_iEhK~aEM$XOU7PJTYcWPke%}aw&PZgFL->V&Ax~M^WRj5w z{Xu=1JFDdc%IgCk8UxtN5Y!%oz6Hz*pz1cYjz)pUARs{J;WYwN?68KP%G zHtsNI`OM5;3^xc)iIA?+@fN=m6-ozgwH?C&yE~E6bHe5TY{Gwg!k?)v&z}5|pMj&l zApSxkMbj#$KmTE$%~hZhF!{KtZ*w@ZeoRf00UDII``}2-`7$aS4k^4oWomD5yZH+( zWx9OgVZn!2Y|BxsL*vg+Oi7A(i=>1W(4x_mQR%NM!!IvQE*Dum4!F#4JxWc$xPWOe zaDp!)pH3N$xX*NnB; z1s0w48!%M3UC`rsmI3*JN*iK$9W=$O_RK~Gr05@FxdWC(`bQUAHJk= z9zmGgBJh$4GlkPD0$2}iq=YuXKe8<-SA1y_-XV`ZKiU(}NOfCU*>sRAfU*LNi7qxS zHdpi&`tTU)s`r)Mxx9WxaejM+e zZJb=*C%=D^8|uG(2l9?0RRCTCi)hNcD7Jaxc9^_SaT5GB^E&^H2mRleU+G$~b&Ve$ z7~8sGLokc(##hH4BmB#jMEp+b>o-??*H4t!>(Ez3g(qN$A~(83L)z$ok)8gB-j*Y0 zD~`IW-r>BzcjZ&N0)Hkg5^iI=)f1!fg9tj0MWx%7;M%H`EkV=vt#faLMU8j7f{^$$ z+F(-G&IS9~Cx%`DoaGu_n#L9*Z=z3IzdqB$Y$=kMjOP;G2rzl^q8J)s`f}*iu&(5f zBB_kmjCV}TH*Fu0tKPU92cu*_8Pp5N3tN{h9~X~JD%26?Wsm8WccjRPW3_R2^gC28 z6~z^h-l$|c&e_c0d0R7mR?YO=g%rKBQb^k(!2G*tLX{F_O6@YE#7^mct3yK|-7Rsy zqa<~m#5}Ny=i79gQi7(Qo#;XpJNa$-L$5~YjDLwXA^yG|DB=HFVOHVmoP-$c&C}n) zj*S#+)K~ zSl|W4#;9v@R7nqE^b^l}+`R(1cV`q6OG4Jt+}czdLrTGhv*~msQL~F(Mm@p!)`IY4 zbL~-MFm`CFch@8y%({<^Ob;@jp?rfJ+Q!OsKI$o|x#sap_@Tr?e;|VsO06RGPs&k& z%?9Y)mz3E@@=&uGYp*n?_>YEq+yYh9crN_x6HE`{0vIww&Tx~9_M*xcy7?QtD6-x# zRX6&LV$6E=JDc<4`uG7#v3%X!uefU>Ai%3AbNx4cFQwHYy8Ie$A}wX}_rmcuddaS9 zrX3pBW&5*KAlb3CC3E>AxP@=-8*A!MuJY$2y8e1McueiYe`e4JO$wcUi7UN4#;_A%9#PwIMupBIMq9aDp5df9c9TWj<4Pm#YzitQ_M-(q$U5~3& z`?G8YN-u1c@g!Ac0@gyD0?<>EX{j)CV*A7&bU9)giKG?dih*y>72ACnW=em-J{a%Q z2(MkCYtcS&C7@4HxNv#HTJ11t%`t=Nl;!FoCYyWshrala=Ve(wIsWy;c$ zROg@>UxqO;-op*2sC9e($^6x)V%aHXDi#umz|u@wNiAD~oeu3i3Uw6{*No(v8B`iN z%$Z)i*)x}76bR}@&CGU!FQLy8;df#OM&3Wq7G1RJN2}5rwQj(4hN-%rAhuCR zA6%;=OZ}z0V|fo=ri!87_2-R$6*&FjWTgy(t#fWwA;`!9ItkYracxSmO$69&9PF9N z%VoTj-?>wHmZL_Lp^d5H1`_aVQ*TVw$iTHZsjOoyil_~elK$fme@JgRn{aiT=g6Cc z)?09g@KCKV$`X)XU$hARjtaXCKNoB-Z8fPW2qCvR7(S@DdU5>{-vryRxaNG>?9rYOHbjgSWrM4ew5WtU@&xBs3I$kQMc#Pvv>-D(gch*m?i z&k->l380e;CsK578s{CMpC{lPa%-M`R=wj<;Oyb+`Jk!5BKoAL2uVK_b_a)w1?KGK zXz@YZ8hY@HJ89G0EyK-|h1pEgwfnAoqW5sXO6VQ7rKn=ye2oE}g|kq-NX{`HoG3(j zT@L<_sxT*evxT#XM}iCtmDaxy-+R|s?tqr+T+Uudhd# zr;8x~evJ}S0`J9CXSC~5yhJJz@Y+IdqmWDWF_%WKS7e`xd}`?{c5P37ES~dsCXl+p zqihlfu1ZfOq0FpbYF&qFbXKktDTxa|ujSGeO8V_}wmb@ZUcTUBy4sT82CBWC25QgZ zipHEri`r5Ri1bux!-C$Kf+oND#O<%udhri~z1Mug;tQHYC7soJIag`vSiTfo||q)$}%cCm_#Q>Dx;oX0=AhWgfH@&D!Zh_|D)|xgUlb0Dq`%TMx{!WehOG)^Uufy@+qTpPnb=N#I!|r= z#D0!$`Q<1(7pJSHu$o7#+r7tXBGMHUlcn;2E)X-7F%SL|5IC@rn&3hkk149u*V(qy zD=2^7i|Hzvw=cM`<}q;$us?+*5TloRs5em2+Ps?5v}jr}pxxEwa7540r+(Aop1lJ+ zC@nvZasUX^o;@}k-oP!R7mM@ybhKqt{6|TaD$~s7hP(t$*IXHihY}R zMe5s<2-U=TG8zG#dCov6&I3tI%`UZ)mvCo5J#vfbLefhMU?G8OF!2||bMQ0B@lSt;-4#tbF$BzK@SE^9;P{}Ll-+||L?k5o{n{{<0KN?$ z1p)q)Pb~-!ycgi}oBCA9@H7?qcJ7s;hh!@h>M3vJV2Cl-W80I zY`@ABEiSM$K8cLOxB(u5Ta+}ZIF#0D+lPLq8|c&3$WEW$`l^Tg=k*pI?Fv3jImb@z z$z<5qo*8&dnf*$YN4HLDx_p8?qL?7QiqC9j^7?P3D@Y?{Z|{!SUU|(M(eR4pp5`v8 zo_fveS7a8*nPk#sH~)z!VSklrhV`yHeerIM^Yj^!u zceq%};Tzw}KgQlzr|B^5)CBDf!KbmjO4zqx0gwrCxl@s%-A+z+4N|mifI0}gXGuP@ z81-^jRZNB<*gmP-2(gHwUI1#2^irHZ9}cRQWfQqXXqq$ zA+dInilF6dnk*v1Fz4VBl-DAPx|FyjLq2<_P9Ppr#{1?gPHtA6Pl{S5BB5)iVbOSS zB7&F<9}?xF*wJFh2#WrJMvnf89U&M!vXY^pSsvl3arxdPwTJ_+G1J!5w2(r8cT5xAL_QD^A?#m@&LE6qVbIUPYC9>yOxqQ3n=D41$ z&Ep5JS^Wjg=Dm~dIh3m;)+Vqn3D4;6bRi?ut=cxFKPvT6naFDs`87}S^!4hP|CCQD zBy#FMq`35eYF3Ky_lSv0dSiASGpLS1SvEFESMwfyl;qJ_=r8--fA^#3S;2~>PEaiZ zNWK+>#;fo0IllSK?8## zUsgP~>rEDKaLokE*qiDQ`YZI*1W`!zDf(y<(W-ml`otGvZF*xKG;=gqM*nWCbM>1p z@r$z;A|1nC-xbgVsx?)`H{`l^T41z5H1(*)|sIIe$4&T;?|Z zH{nSBuIT28GEf*K^+0wK)TiPPR1Y*}5vS(u+Lmp*JS%k@zTFkK=w;5elUn$;1vB*n zYLi|%6UiiuP%Z=WS;{Jnk);|ce>PFBZFwFUE_X|zLT?U8;SqLWhH8HYCutK4JX*ZD z$RP3*Rbu95lh#h{5y%+}@F=vFym}6y$CJSRDc6jDPX~q#SiDZ6o^>IKx4oOon%A0y z9Nm=lJ^GN+eSOLDX+es265jKb_uWeiTqIDuw#QBqlFJt?VBqz2uDzYDRYyoSX1Q8V zp?BN}WGfZDM!EWo27hma$g>}V-cbns&caR|jc_K>HXrvgOctf+=`GZ^LuXdQ% zTY92IzdLy%J0ne&Y>`z!j&2*a8uS(}zNWtO1}3vKe;*(gSCXXSC3Xq>zbqbqd;8({ zDND)9w6-O+*@(8j6F<8?(s2hKMHM@jS+XXRkDd8~ifnmTG2-3advpfW5%rT*(<+22&I^V;dz2~0G8vBJ$ejT7^m50EHdQc8)9_YYckacsgM$-I?e!l6+!9oz9M-F` zzTDr|C8@<`$%KjW2>wG$kvI{Vo?38a1I+0uzy7G`2jpZ*sn?Y~ZB_UuA9P{G*t21P8%y{`-6*P%!x$1+u8k zV?(L#&}Mw;nNwEB< zQTN|Qfi1fHUkrT)lbD4hz=9^}o%TQB&i_Yr)<2~*R+av<8|D*x$v@d^l>f=}l0h(| zWo*r2d@w&|!wxIsSJzGd7g>VUDvxSQ>WT7Q+oyrL9mGaoil3y{sTUAC8#g1Z7_35w zF2)q|7i2I$<*<8dr^IHiwvX7Fo}oDC+uQaot8D8i>Cr313?F{n%1C{;NZD)(0@?J^ zri5rF0*}P;h;Uf(4g|7Xzl{1g4|_?e{0eiYBotGM9+_0fg^SnKawL15A|^-7Eu{ok zI(2(Hyu*lG=s;8rMQO_65PTcza&d&(zHPb7W6vAvykE9IakgNzK5xFv9_-x(JJB^# zANx||QkIr=RCM&III76>o^pZ6=#lb~0=*H>VcvNhw*j0pfu-PHhTzim1vhKXkIoW6 zO2$y92_MitJp;74nA(JM*X?|h*E4o!OY7uQc%qDa_AMWp#Wgu^^Q0_8bhc|N+#Mvw zB`_1UY~^OhCK$h9MI&jNP0ZC|Q(Xf)*YMf{kih0KI>sp&P49ms7Ca4Sdm65Bsf#`{u3crEMm|Ap zb;u#fa%wF|MFDKJMu1cNlCp7Z+L`iXMnjJTVaJRdc(4x5M8!(9^`>tnywO zU(Q4MV#vA&;=a4;$C1|Brn>r>!?iKn+%8fP-aifRbr$N=aml|p1RvvD6zELiEEG&M z7;ssB)LFNHh(o3Y9iT)$$NCoQy$^KSFbdl3G|aT;!&G3PKuTKW&;L8q(tp>lJ^mbf zgYDe4(jepH5Y&c$lmAB-t-P=H53Yny&L~QxG_IB8r@-Gcn#CTA0$dAh&(Y1(iGjmc@1%|GdkxD zpedlgo<;(l{&_79N(bKIBch-!4{D#|C9}Pf=24m21$&Z$mmh^x3E=o_L z#pyrjQWP*TwGGLmY)8Agq)W2Rx3QZZlz!6FqJRQ05;FH(MTe4a~Ui^q>x zLjL35@&DyV0LajRm|4OPwVRx4!(q-*YHJ9b3wr57YQ86SX$c?WAp>qs2xpc$G^g=5$FQv4Ga1@~I(j_Te ze>M}z$@nmD=3~i;u_tjhc^*a26ZRBRKq^sBimVE{YGHb<1wC7Z!pyp;-B= z->&q})cQJE4p)}+_kVXr{K{R682fpaABT%bVs`pEs9O>goCbXh^W_LBo4wumxV%WXFfp$hm-C?r+0Fd|v zXN6r)?0FGGib%5l)fPpZ+}1n}^A=l`)HxKJS&?5R_m-*=aMORdO;cy)i7wm9qmk;$ zt9pH$8djsAYLjTVFunU2-H1;xB*zi4G5ZEL7Zh={bIVs%6rS_FYfovEW%v&K0{l{Z zJ@g+KJ{TjwIMVMiiB`wPDq*9mpv{k)CX{A#+^rYG#6MhnJokhDn(6E2sC!F=4A50T zPMa?30C8(SABxfDRTiSziXb|y6z;ouK$HyKH!5`&d&>nKeZ+l%B$5LC6ET<|acFCE z5ftm7LQ0D9wF_}P^BY}TbL+?9`6hX55A(kM?C`GaZ;U1t`hEpn#IZ%xmLbXNyCsMZ zCWl9zUkI%3uTy_>&*fu^SnG-NVOxeU00effkc5UVw;2=A830<{* zn%nVcGF#u}=4XBm-oyvL9$pkXuDHgusy!n_HU~sw^chzqKb#ked`x1E*j~L@|7cT3 zyGCa@HsbvsOq{pVq{kytq({-W=sk#P?02At{)-_&(|d`{P{eUqDJTRvz1o}te%})M z_Ef6Y(`|CfMaA4(*<%+O9S9_<_t-zw{X~359Hh^DXEJ zwrre%=E0%dZ15s7k}lszT)1P0_lzJOT2zfz#o^T~1!O&DomUd3LuCu~2j*_A-vudB z-D#1y5ls)`3|CapK3alUpY-AC$KbCov1flt+}sMgFPCl@&MSOf;9%1aJnch%K{V+! zl#9xhprql~HND04A$KO?A4$GZQDCeUx>flkUD#8#w~ucEWLZbm<8?Q|PU*=4jS+UY`55z^ zfwLf!#z<=_B(nAlF;TpF=pcYxo^X6M<{K0`-|Z(IBQpGkdxyOMHr?CXb03TxHjykF z8Dvl39r@;)a{EUA_ut(5IvflkE+=>By+J*1HBXa*HoLLxFhkMGB1+)4=54u~avt_O z{(WKR8PGn@rF!rCFJ&H;c7r&nRzz~JE6g01dP621qh zLSScaX3rXEvO>{vwv<_XK+N+=WiGBen5e;J8eyz(d-|5+E4#jz?~nQ%=15>=Xa`;@ zmVVj9LnRhwR0d6lytvMs*+s4QIG>ur+uP$R!I}G>5m?}37v*T60J5q!#Yol<0zj|p zLT;@yvT1#ib=ICj4q!50~B?nsxfFeU2^;A9oNX(&%~kx0->a_Un@>k7hqy z?0SiOB_|{>{Ra6Y$(Q$$pe0Qw`0%R7lGbaiXAI^)HU9*N=0f~Edbbf zW&~I5&P*Iov$t1~%hy~xkOo=~s0s-`OCTA@$=K`HzU)Lw3Ol&OxUoah+e z`P=pi2eL{7U$-x(lBwSR7u)5Ek&rDUPDvE zm{Pg2$70qaKR3V_GVO0+@zZ+W)WI9@pj!bd`2>E48!Fv_Jg;e_(i!ZV3$8KnIPaGF zXBlkhI(zUmUlMhnIq}|=R?wnX7wVLoc0GTONPsB`8NId7p*LYUW}fvpOlBK1@m%+E zNCouH+4U#D83sO01p&Bv$xQ^m7yLHGrBnU6|KWMJAT8vpqDNjTIaN9WXd>r)UQl+n zW9T*dP%fyMn6V{)@^CExr%V3$bz$0=dl}Y?ixFZSy8-EM5jv5jsu4}TxANl=-I()` zV?X5EY}tf-sDo<4_}hDzrwc6|9((o-ge4lNqVy^gOyl-ebF10WcfMA0vhv@u=Q`1>>$=MO44uQkN)(0x_ij4_9Ug*CtFl{ z3iga-s8dbL(saqAz62lzz_2s_k9FJu@dwG?p)B%2!pCI2ZjPGm8Tp8GcZO7aKoneL z6I&_wJqs1HaCzJp#PRFbtg6u^<>1-IOssp4z>60Q99sq={0nF}?X$LHj(9tUSXOHi zX*<6YqXZHe5Gju)S4(A9_2wnVNx0wUlR#!j*BlUPPt2jf8&C8yP@gfo23ft`&oQwb za}0G@uL~G4^nt{g6%8g>S3M=|F%J4KsM9{R$Sfv>BCEEGzHDY%HkVLrH%6DtMz-Yg z(zluI98*~1<)5ocz`py0-4*VM!Ms3C*WJ31}y(v}Ok+@NyI>&qOrIzr82H4P9W-V{&dr{Nlm^Tgn{tF$YCrdUIg$07>pa zEPm#6{TZd{nq4u3(r>Tx=lk!@hjJMoq~eCcF=sS6PVOV)ynx64p#y*Ixa8E8QCw@H zk#0_$>f7!L6ZZM{l}tkSR;sj4!^amckXQ*cy!P8mD}|uw7L_s^qay1k8xp$0?w>CU zCO2KhT@bEZ{3iJ6oR%Oh7tRLkfz+m>a7Jv_q)Z#35McMjDg{UQoUZY^(X{Hq?{B;4 zfQ_?@J~rB?5KeLous?}jUGpxTT9Om{XQ(1&Pp0n$Rkc4&To5xz_me;Wo57g(OGxPm zkcU9i_>MRQZ1T?QJ2X*h7YQy8r)uWS2&i&D)$@}h1x*S0kt+%w3VeODJ@cxAca zX*dxLvOlfZcNW9vn%t6-{sLB zQ@0MiNlPuHcsuURpOG}!6A$V9I!_g2e{E0>o_6V>3lI;ttVr1V6EOXP^~}jaUi^Sj z3p!Rht86Ax^zq9!q*T&-QAkCG8O86!8r}=^h?4SuW z8g?kPE@4Bh~PM`mmg^K7XxR`F?nu2Q(I*GG6zXlz2U!t6?N9xZ~lo(uOP^B|h zE6#j)?2l2Uw}6X(aBtzvvRkgz8k@%JyZaS-v7*i4V^9ANY6{Jt*MgE_lpiR~OPMrH zC<}#K3Ta%kbt@1%uk$(FgdvyJ9f$x9A4XAGPp(lf61y=TS2OT@jpJU=$2?i4@iEJi zhA0bFzs0?u>Jdqq`$hp~#Gl1p#p{0iH?_={+zt+c)WkqZ3x zRPJATSq&r3!31trzOpO(zPJ09r9p-#cL}vb-9>qC z8Q8}kA{d@nyJ9a=g>cB|Tn6GCoflxN?257lH!?Qlw5QqQY(g0nK`ouBDUcQSzcF|J z{a;*v{EI95H`k9}tdzeJtE|Z-!(9676AE4iEwYb-*Xx5U+Ui?tGk^LC%0JOFjb7A) zjCgEUP&J4SqlbT_8`{MU-W#QF%%vskL{zeIKK9j4YQ2;Ogfybr6AUffJiUD9|Ij`C z`$(2_OVGY&hoNVx_WUT_lIlT!T3eq3xTgMMNM3pTPrKs(3mo8oo}Iq`&pX1hqVO*U zF&WUh3OyRgcrHftvgsci{l$JmLsrS1+a<6fBR9T~rCOZIv{pQXR(Pl$h5>J)#9RZ8>vGk0D4SwpbB z1pz|a9NoYj^m{%jIjN}`scmuy(rzG8YBX0vmz}K6D&j z_6S11(Bxr7$9RVM32q4LJk(w^1Yd`priipj6vuwb#SXr=MAF z?pJOR|MBigV62{X7!O-*MvD4=nNxVFT-0QB06J6S4y?jABq%oSeMPxvB7LvQe@_cu zddwev0OoZ!b(^{UF|c&we7I9hW!P#OQQ=kWSyhJIoKoz898j*YYx)6Pl;gwpc|@m` zEh>`R-eCqBel1P5JSxDDv&$_1+z*xK)P@@h9CBOLmnlAr;20#|hnGnGv8ar~2j6+f z1K|u}0n)oJ(+TQO@WSf}|Ag#{+A(`{P*AF-BiZ=zRI8uR9c6!-2#qWEz(W-H{~y0U zh8H{ee8JAvO_zJ(T?-Q`vlVVJ3yb1QtVWP^36bb#yc{c4|677tPj}Ew6*CWl&!v(F zTqA$p-{7z$?tDj{qNw2D$xDd1OXSMuO+>kZH3!i|@jxXfP^i;io)5jc!}?XmYeU$=3$g<#3jy^|)SmZ?3a(}TY)0q>!X#dXn9rR(+I^iL8T z`3%2$3QogP9-a0)#ch?8B~toJ(}-vY==E*jEgC=GDyGHgE3_M$0%_Jh*W>kj9oMbX zI~vjO7emu)J0gD7l?oAX&@xb1N9oA70XBU@i?r z;iKPHXbJ&8TYyr6?Ydc3 z$A8?yEuux%Wquc1BEb*}Cz-D|=aoQh)2k~yIO5KrCFMFXTAz}<2#&op zS>zLPdMXm&+^|X1B=bq6zAQS=q9+Nzcla>3WzsZX&34{AqtD?lhGRon>k{u3%(R=f z6$G*VVE2af8a5JJy%P$YDc9jwD9v4$qNB^pm2G5D7r9dcM{aObe$P@|sjk{JU|`xk z6vSKr0%JMHaK;fNsqaC^x3Vp2-hdC;+5MvJ=zVg7(CK<6fitRR8u3+oE^hZ=A>*^IRD%w|B}tRu%aalO<%4%~4{k z$EdmT)oPZ`2Q2r?0QWs|Nv;af9ta#WUgUYqY|Bn1!ZjqH$78qhq!k&U(YQ$f-=G?R zqpRF+vk`ab=e?>Fc>I7loc1(zNKspMNGhRWGuOx5j&r@CTb3|Ox);@@zHQJ|dK_Cadk7^WeE1i)^>Ay3dF=+OKk@jxConQDP&P93nl+_#g-RbX4>{iUA$?9_55m=R5t7YPe(4$U$9#K~*k1_Wt z#kJ3X4L$N09H9*6?ni4(?$j8a7xI69JIiC$#9K`4OlmmqJA=6nfMGr9iz7((XF4`6j6R2&e_K4eSMyo8D*(B}h-D4$i&WX?TLNjURe)lyty zS3WE|nkq{}!dUTgVTJ%ReBm09-s0g_(&qvF_9pdb%p;FP=4qsIPDaWD22e)By0v;j zZDGnv>wYaE_&D23&^fK66`R!r~lKUaS_9`iRDpV^96IP2LKS4Taq4x@4coI zAF@xIZae_W`-^aP%8ju_dJ;(rH(69+@0xKb!Yw1+_E?wvR&wTtkUYaDSi{a?M(zXw zQLW$hv4sn#9aEnU=7Dkfe8o}%KLK2t9IqehHcxwg4&IUQ1JtEzINg75aj z?Mb@B3onNiWfC!x%gw}}Q2rOgM_ugjQ5@cCYmdZ)V@ZV+Q<^(g4JsRvTO&r~fxvo$ znz}iCah~?nUV?+4LlQu!dkpZoe4A~+J?QhKmjM|ZO|uT@&K8w7i;Ts(+d3B~4?_{|9VyU#~VQY_}4%ezDh z18HtC0Ody9X~P0(M=dsTVF278b5SZ5`6Ft;a$Kz5Lw5VQ*#nDvE2lk*1jRR?Klh2X zH*{4GS~ILX2sE$H3Z-#HdPWbArJH3kka*ajZb0riA`)2F@GEiCKCc|N!}V(&?rYAW zfE~((yq&uo$$75$q{oLHhCWyyg9-dUzA0i7Ma`o%ZG%)Gn=jDFq+oaT42#Qrf$A`RtQSi)#P63;X9!7pKoz#9g5 zv5$~6w!lQ7c5PQ#0Ti}bZPye2S`!$c@NLTE5H2yl910)99g*C- zH0hR~V1{J-?}}n3(tTvP9PHLotM5eHt}%Cf>T5$1js2R_osz8gwg$EJ&vVg1gtfpy zz74D}n%51)j@ERbNEtS?A|FQCJ(z^_=w-(^#>`5ZT7|EIG^+lhtovnx=dJ8Zinc~@VWx%;1=EsGXx&oE8h zFFSXXb3tc|xT6lPWU&${vi}!f4c$d16&KpDhZrllxG~uAOF& zUx-@e8ysz7kAK~oD74Elfbe_V4w5>aK8obEs(|tvP$a!@($Ygh5VUO5RE5Z!rhq6y zo^H*fPTAG$XS)nu?@8HCu|IQE%#I5^x4S291WV*(VP5$30yih62S^>la%kRL^sw!) zMNgnX&@U1#v2J`$=vancN%LL8O};i(uO|sD&xa{TL^(QNQui6n>z@p~i`QogEXUUN zo+z|b7yNo8P*Re*!XnJrEp7A6y~hwTIIst3^~2o)P0BJZO)Ez-^zEW|lbAO!@lxns zXZj_UNH;RU_K4aXjF1;!_$4Kj9VyH-9!oZf0asUK6;YwX^ovm0!Cln6eb(uxqC+{+92l)L!+1_~pa`8&pyA+$pcTfp7x>WD9!sMIjgS5O z+C-gMg!070qOLUEqDYyQIo(cqy^IaMo7Gc^q6y9ic6SWn zNs?8x5LEOd-C(&_*%)}9uQds{TIV%G|G)uj`{t8dKzHQJx8o@Tk;g1 zeO72?*HyS&PHkzV~M-z5m%lUIF6V4%nIJ14nb_Xw7U;eAdQ~X2} z`Ds>s&OWs{5G!^q>s8Bb#pao2}&|pn=S!Un)GWUL?j|Jbi5N9YKzz?4(cm2oz@@6EKEr|K#z1_H zsXc!O=G2Dv1x=59ijx5wnefp2oD$NHRWheF?Vi;76Q%Y3vT?DBh3I^>?(8QkTj^!1 zuBb0tTAWn2i58%OIk!F4g_ae)sVH(sxlx9IsVZmz$&P@I=L_kkKhM%{KFt-!{a@_8 zcR1Vs|1Yf7Ql(T|yJ%}u)v8e|=|WXeZLJt-t7=53y&`F;y;_QvqH5PjmDr>;i=qUn z7$NqEBt`_0p7-Z`&wZco@4nAJ=emC9y2o|?;L4SRB=5Y&^YwhJq*hPv3Bh2t^XrrG57H@x~w9CF>Oji;jvqgA3 z>!LoUa$s+A{0g~|P@Na2eTls%ycsqZ0&09#?{05VV4b$ zhywJ$bEsB;#r+y}ANX~PCCJH?&cv?)`vB_6z9dm>XBP#YoQx0X3@p##Vu}NDl@_68 zq&B1A%c_4(#@vv8-V#gv4ic|@zU^0^X3xp9UhBL;9Zy70r4rnF147lcSDs3l;!Vk8 zN^UHNs7P~%Zd&FvF^YM0I_Csq|2qLgFQ1H4)o2Hjh&xz zL%OhiErc|~Di=T}LCQ4e=u=(RL-2K}!RjAJ8eZdhH1DqbdUZ;G=luki99Z+ZpS5<& z!CE~$q-6&-O1~Vq0wPyTpq=2p{;-~|9$%|L2gQR-gkByK#t6t=Zs^P{4wUPBYdZ;X z0JS&W$-m|#=P~&muJs2*2O#6}eJv)?+fh(y3jyRkjI^B&tAGbLju5d2ykbb#gL|H*!kfc7Ow*b3h~>$)o_sA5-U_x_^mg#!>EotDrUV#V9p}^8 zB8m{Fp*p!sS3Qp{OIc=j3mQjg-v3c){f5`cZ{XZk-$O~5J`?X9$@eb+Zxy(Sm8}%fiC=I3q)z}8Pfk@d?e2}{$+ctWVR^ds(5+tOBoso z1C48rGk^NI8J;Q8@vfdClzLa}QN|>csckc;Xpjn{LWM8TgP@b8PYO!Pl270lDis9Z z2aA5ktJx0l!mRd8^Z3RnYaY^=x~LcMWGMlspVQOfKhZ%4snwEEEs8XQm^`~x5cs!l zNT}}ME!0eJX$Z`E_K!7^8ZIwm@#?!&K_Iame%@ClI$LgtluaUE+_o)s%3j{-lQ;Ts zOXsmuqN=Hu^zd+gp6SQD(trcCT|3`WEqs>d5W#i?+E4$z2+N}p+ps5@S4Wh6DW`xM zXskBfvT~|TGV5$(uJ_Fv^Y=Vt-P76HRy8ZYZo@jm6vI`+z_GzQF^e>hJoohE2$_Pm5u?vI7@$wgvd93wVc4h-I^3iyk+X;md*KZc^n1+&uuj0`3GlpR&KzBwxi_@Ldk}5haT}3mf(M*zB=S6FOv0UT!+{%3VPX3n8~VE z%Gadcs$A1ljo*6P@NBaju5xYy&2 z7c}8@Q`iu3SVTGa^hzycF-G0QfId&Tv|f>)VH+dnbLUrknp_J^wKC2_p(^+--RA}Q z6Tpc#c=Dgb%lsGDu>?*K@ODl#I(_!o#QA~YaC7tPLI|=BE7Su^l|eBkCH#T98dM8q zoWe3aO3d$ZxBUAZNjgBCo@0weub@Z|PQ# zyEfTSicV)905*~TT^#oR&~5g?e{x~ZjbYa;n}D+XgHmM0V(VYFjT4!y4%onfLUNs3 zzA@k&_&)5vL1VlC0jxYHk=5(xH)Hs$nmFGNu|_oi;gpfS)_-j7IF6A(S}mYfLyppG zCF)B}eCV0o@Sm5zf2p&6_wta|-1H0mAov+$`$Mk9{{{da`oC!Al&TnAz-d7a@|P_b z4EPjv{&&$Eq#>R8vKp07+!J^DcaZ41$iE{^|DdIz0#t6633`@t5bR3a)3}S-zXk~P z>XE-C>AY1eA0U*&%I}_)c1CT7*%;8q{mb@>@$pparccOtpYi%U4tRayFCdVLlBf%x zO#&#(4U(;%r(E|u6PQ|RgU}_sf`?OM?epgbNduOW?M>9Hjna>gZ=eacEEzM7(;?!T zq?{PFn%bsDD-#ZIWoV_1Mf8V=Cd9ertb;qG7T9Mre|%tc3v80Gw_JdkU#>0KzLNLy zk(-Yagv?aj+gbqLPfgS?@ooX0fhhnv~4fOV_}|AstDU=njBN{&UWb2 zp1*7dk`7RQ6S1E2@V9p>qic#4zxZ$H_&B8IF-%WL(KiN}w~8u!a|D)#V1oUhj$Wh+ zc&!`RcOC38chL_IGC+#1V2+lH)`5AD+NOrDwPAr7I!6rOrQgguG-jY03M)Dhzwpii znb1gXf&^b$Cwg_n^$xq5%uveUaQkh`$z?cla}318>TgkCNYZYOcMaQWvo*ucl&mSkAP|GR20pyq_ES0euW`#t(YGN)m$jupdu#Ur&aj9 ze?14tLC%IL&8{d6h=?}zw%4!H@qm}_mIyXzNfb1fW_vZ{h9u9QUT4>&!-C3I@nI%b{bOvg2 zLMXTcDS;mnu_v{}%2oNr@TXm0yS961_M+Ai9v|AIKs*dL=ex*VIthaXxv#@7>KR@e zc}-TbS@zu{`@s|(j$+I|29*n-ASN_^R271EgsAwe@ zkGG&E(d}t+Kftk;*&4JFdiv+Ut8K}cMS6Wp!syQNA4lZ5+$*Ib*|J}rj@{*rZ3x7e zJWNTLKasUHiSZllt8Li4S{j~e>RaTb?&>=4>>PTj>BBY_I6IQKp>aqV#%>_lYw zY@G3jt@kQt4a75ytPRX6Wb9_Y%3*wSj<;~FV2`qnF`eVucWN}IWQLnG)uL{M&B$dF zju>4}yBU9dqW?NOH~@VgB>nm0mHY8$>{Mig#E4jpni|&50&ND3<1bT9Yg`4Kr?FDv($!z_5FxVQu&pe2_0 zrF*pj3NW4aL?7g8KghXQo#F|9hzvO3!y?ZDZZ-7yy@1H3pj<{kCzRLGWr(!cE~HV_ zarCP$;EQniO`cS6?G0RQm!xPJQ>BP$Nz=oIE!Z%xh|emgbj|~ur0IC%2FJvi%~ZCz z?F!jOY$TXkR#^=6bpP=MsscCe3o)f6o&K`Her+QD0{mzVz^NyD1Op*Ls{JwB9+blL zZ(2(;+5A0)J2h`D9cmhfXOxzc{v;U=VN4u5z4c)QP{sz)m^f2v|5zcS0tEjhkjQ=b z^u*y{9=s+1$^jGD0=T9Msq+BFgPP$3UNr{TH91%ltREYAPo}hl8KG%_G>{6|7f{Ua zq8??GreItkEmwED)1S*BVAE+?VJPI{XQQT33bQU z5|wdb`IPnT2j2qliwp?_)vamL$*Hg5-NW%Oc(V@oP^*2J4o?zA0%0_wVw!+P+=ID# zRI8BHZ^69+`=pE%FCAX=Z@tXHe)jS6=EBJqB_L>x)C3l2(l0bCF`8-o09WI7!tCpR zIr8G!oA00CT5O2E-#h6328A~9Ui}C2`^uh$wWE{UOtr6H=BGxhN`GOzLhMUzZ&N<| zGtEAMpM$xX&u#UL+lgH37rey3S2cfyy%*|ljXIk5{=982~ykkaDXJT*b(0K!uhkfjED0keAxbm)|=Zup$89ZuG9z5mIsZTDTpy?MYL?W z*Wyg70fme!k8Z9)g{QYkqpfXq$|%b|sg|Vy5G|70i3z_Erd6ZWab{7PzsHCJBoT@u zzipXUXN1P0&y_z{5>;Q^(Y4~ube$M`Q2goNw-lT-2$;&kv04(^28G_zrtRBOGO}mRj?X z?4^#Iw$rP9Z>esv+#IQ;%WGCiyQ^3b7jgG%$pfMJ7K#;NoiL8WzbFdJk~dXb?3stB z#WknyAe*Z}1O}PPM*@QMl--%&86qcv;W;z)x@ce1FzG?<#2?P9eNj3E?%9hXy2(c1 zQ-cm2GiZi0uu>TUJ!r-q@Qf46BS?u(Tq6S8OI1+aZd3DLHjRm`$v#K(vU?MwN#&Ci zhUifI*Ejc(8ynDUv=3lf13tP0QmbW%QR$v9VY#-weq12gk$GFohS*u|w+ini-<)C| zy~t+Aus*$gCWPb~J+~{OG>x;h55DNIj zG-X&!dqzuKsN>k2xzm`qZCmy=UuRl(Tgp9f=LD49-0Eyd#NE_0NM)-OTW}gS=;OB$ z#WPErOwG~pq7d{4ie%Md$I_F7FVz&$z|SOksS%Y5lfGYzbFYdFEp(D5EiRU3_mUk3 zsp)k45tbNouqIocW>8+ZOt`S}OVbnV+z9t+z`|l)8F3D=BRf>`q?U0XCDTVz+*xkHAH@0 zlR`##JIyWaIV@D~F{jxBT52RzMj&9@BP#Oj+gQe>*eUCEH-~}r&$i1&wgI80&gr$# zaUYe~eb{mc`On@?JC>7aLBj$vJ59;3i|6r0gEKA0OODN9nlHOAz<<8Q??(Z2wRenN zne;a3OV%-lu|_Vem{Hitf{*1>?A!M0`=aafFDK{c4qS4< zH5w^8yz3Z7n%Ai{FSY0&$Y!C``q0w}7wk|NSjF8=PK{~cNh@4C$xjC@YO+o94o8n+H z+_xTMbn%hQP4najCJvK>bj~<{7U0HDitEkcVPFl1bw$+BUd|Oqae`Ai-bN<#dBit$ z-G`#;(U~9#rV1YG;+ZHqH+2rk0dg<%c-BbcjDg=(u1ys=*DWS*(e`@x4x8gb=OHxD z=bX_eWGPL-T~FFwR+a{ZSRa4cr-;iy)d&MCE?C8*dMkJ2+EFK6ur;;eep!;Od_FJ| z7|>&j>PPgo)zX{a+>f9GAJN+DhnAEg*KW6j$o^SITHANv3LAuL)M_t(y_VfK-vEMi zNOkt)#w>3>30jA>6&Gy}5W7y!Qv5cTQA+48==rk4i4uVpDqiGyR`4oAqL1cCH)ljq z8wMbFKXF+F#1&@^4)lpNo@A+?f*SmkO|6UAw$4y3bRI~YLF^))L^(q_#t>&|G7;&q z2(8m&ZhJ>tA=Nv_j`81#^gYMJv(2_HrDbRJqx#WcduQ0Y$5~kot3_GZo24_((dMZ( za`M=Doc($x(<4-1|K18#q?~y*&lM`5YglL(Q(^K8B98sT8u3_(xSh-$*m)-EG3Y6u zt`|hI20y_t0ZNxF45C-x`Qgb$k*-EFtI!mvUQf~Mxh$>8kT1siP89VWNLL~97trK@ z{)0X~aY1bk&O$EK3DR-tX(bhm0;>_wlI5&kVjz9WQo3^F8`{dR1N6&M?iCTx7OJ`J=mqP5F zZK1IcdP32dS>a>?xJ{$Pb@AsT_JyK%jj!)qoA2I@H1L9ccxU;}E4wG&)q5>ts+(A9 z54pDLR8d`Dzwg^|cSYfc$ zAh9;!2UKLG<#I|Vy+Jc`CPBxbCURw;d_PG!_5J0IUW)Fwt%sv6S3gKfa$Tw3$ zwlOs|Q_z@kMFE-U1P>=CAj8qWrIu)-wtIC&d z^$#?;8>7|lbq|#+e>FmtPdZ$X&73c`5g2}B?D$Et{TRF%HURd7R-szQsFXkRu4M3x z@V5=8Ct=Gi@G?X9c&om556h#AuH8K|)7y8+xqKT>#hc*^tr2IJ`cJ)E&y}z!sKyP1 zsvl^-GuMA1kxNF20iBef7H$UT50G3+hqgyui}Y(_x~=|?*YB))G-ekH#g0{L=8=EzhMQMResecPbsacIrI{>OHQE3Z}H!1Ij# z(VUo54OWS(kGukYmp5;>8)X3=jaTt(Z|_^>BjCd<;Z?{SB&sb0e9DzJX{3gyXY9P4 zZ~fI{4rhKg9Zc`vFq_;#YnGiyT7>q;R$TIaLD)Nb#@zDRKYghUf0=(Vr??i|pIw@S8eD=aPwJ zSuo#n?#Y99VfcrayStwHWQQEG@%M7cm~VyH-U6ljFP^-M3ySXhqsbP(Z$-FR#iidF zPgZCKfgGGeK*sv{6&a=;Lztn;O{hb=g=tynAN2ZEFDm2_1rCYDmAvwSD}SW$(5fHg zwn@eFn_E2$+<>)6Ho*G*Ok_qN2J`vRH4|6XAMI2?T0F%g5=FgyCud~*Ld7d^CIKdD z2P(t#7nn(x7C%@0v2l1x!-i|&8FPpE{j*Fy>JIrR?IpvueJ3L|@m&MFW7VX<=&X-N zk_QKnEtEoNnysc1ltux4#LFFM)nB&u-mGJ0vbmoo4Qd-Siof#*xG?iP*j=9tR!%sx z@!JAR_2Tyg_UW1gYCq+6({M#TO(QI$V0u_4q1`*Ho_|fhmDv?uxBr{-_5ayFbr=Dx z36Bo3E*p_1eL8@xMv`jv`XWbe>7-5D!lbIkbj+#g_^gC(PT?gV;0SzDjWRYm`#rdP z;3WncE!c9gCJ@=}M0!saog&|hm2R^w>apO~{MA;d^@)%7|JIgnet45{`VwYei;+U= zqW9e5O)9&Ue?n=kBjdSWeEO#5iBDXdVy0e%riz!xl~(;2r>R%JL)nnxj2+Sme7gdr z=}|Ikw(X(y`#fLAoHUmo?7}SBMfFzJ5UOf13Mm!{{n4UL6|4`fVDNRAYgNN7)s1^y zA5siuf1k;2dI?%SyM0dL=zqYi1bhr}w$>wS?gH&_KiwCcPu>)~zl4IY73l|~$YN9j zvoQ)O6d&$1DrA>DMc^Wkm4C5)IH~cwF|+92V$KbRC;zfF`ZrYpFg5;Pd7nhg3~HC7 z57^=`i>!CucIp8jAs|!Hok&-5KrP=(ZL_#w4UCHR|2m^B7bA34b&dS4WtwA#*2sCA z9+x~a*9))jT9uy7W-HH$`N#TSjw4(n)z2q@H_w$GF7usT@AA!;8ex35a?1IP4+D(V zir&^KUfgr)L05E>evp@QzM;w(DYoxM_QJhquP48=1%S5YVs(WI(2-AQa*>ov9$R^u zV~dg7kIXz}RW;$$;p7!c{sfn4pkv$n{^dl8}M& ze~Xg*kLi_4>n9|q`C2D$&C0csxmo-jp(+m4Jh${WmvX&tSH0u;=5cCW(y#8pnZ*Kx zJ-Q1n(Oe=Ed!)+t!;xcU*2i|TKArBKJ)5!8{$w#S`Sp&6vDtbfxOwfxjMtlv!s(1} zW>CF`-81s& oV*}>2fDUn0zOxJ=JJ9KDf~!PGqj9OV$m?gFi1~4%@NPYty=ur) zJ=DhcK%+lowfotXHW*CISnA`J1zR#Q^=huR+X-|m-QL$l6*8` zm(_Y*n4GgN7ij4b73inm-iS?d>>0Vsyb<_m`B<}S#P-^95`;dGFm-~;L#)DvHKAry zI8m)qA!TkUwkB*NmsGq&xePS#{+auvE3a4jS?z4e-7m074Bp&$9)Sf-}=e%_|B> z83a2GAPyLld~2vtF}7{N_LDvOU+i2{f=u9IUu_`wUtNC0F>zUED}U)xg1ULRIG~vV z52LIL$l{ma7FaR#?@dLeF+h(aGN`LZHJsULS2;0Bw6|SFI#ra2uN9NHF_Qk5i}Mu} zG^eDeRn&#rB*XVqqFZAFEr*r2lqukfbDMfE%V zFHlbQd&t^`+7cxJ6I_CRmH%cl*|UkZr&K@%{I7%=4Zo&b98TlaOYf2Q4RQ+mbc4hV z<{l{V-N$)s+24dI-yDMJGzN^Rm;ZvFjc)t$?d=;RCQ8c0$JHm-R2424B2bJjsUF~E zDl~@V!6a8(Q`jPBwKZ>P>D9?u@v&6I7Gs2DMxbdF7;@2ZJT#?CoDXi{h+=vfTm`U-ZdT9?rNfJv)Fpw$oT2_)Z2wj>VJRtzItb#Qwa z`q@Tz_wcPq!rIM^ft~ATx2CO<0zF%`De>s+f$M{QC%iYV{TlErBy@XS`1Jjv#i4UN ziNQYlZl(!F4?O7PuS=r|!EX8fp})Q;*gycM7>jq)CBo;1EIDTMja|P^-dac)^t7M7 z_H#fNkHZ7&CzBgSgLXDt9^|Ay|1dK^7Cygk2Q>KKvl)93Q`&fW4KY5WXi+Y$OO$n; z%YST0nfG073eY=QK(x+$k38B4V04FXWr3%snn`^gII>VcfI^W=@sKwB4PvM_Lh1!z zH5Ok&sxDDC)g9=i{q9UvuuQ**AWl>SVFY}P1~DgsY3^ISVp#^{S4v0ol`p>L%~uR_ zo2s8a2x;SQ-|LSlaaB+l7lT5Kj(Kaps^b~)kzN}+ehT(MnI~3pem$<(wWcP(%3sK` z1|RD{aLs*YH0&rN9DiBlY!W2HjBiEb$}--n!EXeUO6zOdyxDzr)Z z_FzPxh@JC`$8}xJKA6wR9^LP+^(bi69#`_;{O%x&+ai+1(7bM=1mtfbETi*Loa<6R zYA0BhRSoWv)T~MJ4*mv8mI%M z9(=Td6h@V})>NG})SJwID@Dr|dC8e{cfI8}<0+VoB8Q^BYyV}7L+Hsgy=^=A;CQiQ z@fq8gFb{{Y-}J|Afm0>I9larrRQkh(5nL*Iug4_w`DRJU{b8I;qgXrmQK1TOm@VZOa+EX$% z1)f&smh4(1YO=kaCqbh7(+8w`t7pEweD-*o)GS950y>-J3=c_O@zb~P+s@5Hq=uI9 zryIvEiRpZ9mFrCeSuT9Gcp{h*l5vagLB5kb5bf2hGQ4qD=`OAf(#-n0XHthmQ} z6%((qjtj#Eo?-Vl;0>G=m_$6S8eF@GxWbz?o#ognF3^Qo3q$1<1%YgK(y} zyjDC-+B8IF2IM>RI-_9fsS}2$Y$psH-|}7! z%YWu&sM?G!u78$(6(X=yPB}LAXO|FJUOJvxE@XfSS*<$~3D!9xZSHY4VWY|Q?N6RN z%MLmvZn%~7Q@!W^BzC-(H*FUIlG?9H%;or)?)|YGTFecKJD6uKO|;}~qOW{@pJbf8 zAaCWHoDW<1b<|3G>13b>Q zC8{*g6|F7+JiUk+#dz2iqkbO1(B)Ww8R#codhCo<2PAeo)pjK;Th|t~Dgt1l_c=ZF zy>4|1O})usZ~>|GP{utKY(3w>2CDO=&RwNApoQ7GzyI|w8&QtwT25M}FVCwCg7>x# zjX!+$_JVzXPxwfp(yPpN(br6M+$&b6(5{7uOi-pzqfT&T_a|PKiH}(v#l90Yse?_m zNa@H3_6=k^GufY%N_3D+PTvKNK|s zU=4wQ=4@yZ74Y2x(8k3PETxhq&|`jG9LkghdZPoSD54|tpEo`V-re@xUqeNBFQ2|s zcH_QK#RB_ow(Gn9yjM{G&uEzKZh7ugfW-{3L_IWuYIV=qTq5oO#{Q601WdvJZq4WX zug^A~B?cLq~%WZk%ow z>uB47Yxdv8UY$Q~k*-&#C6EUoRxLSdwS! zWiycdv@Zu03m^%bV@F<2-i&Rnq;cdG*>Qbp`e3Vdu3nLNKTw^{A|6V|7nlhNX}_z6 z+otOIx~|Fzt`WSN%W4)-Dr=R24Hj}y$?=MA=#afhgl=z-CAJ$r#Rxat*p8}>+$U?l zgtz*eDI0~3vH5h(+7ZRY;l(RU`WmaN@gc>Eeewx zyev(Or_2^ZcHM)6d=)yA)YV3&Y6D!+{zRptp-%u(Gl1Wx#5VxC6#u_$yE2Fzhsyn{ z%)q~Fk$#K>l~^n=;b+cms;$QHeG5ge{n@;kuuliy^X<}FQEJtDIL_jq?AW(Q4g^5C z{1ompZ&Q7hivvwpp0eB9${)-ZhD^WEtC8@}*n-9r`P@Nrh(`zS5OU?5_S*Y5BbDm9 zdVUoPWL#a&iZJs^fpP`%b&1xtd5XA7$o;N3QBhtnq1@4w^-a4(d^qe;;&jFiAAyhF zKir~*xJfmP_x0pMhi!WvFIwl(#JXCg{NihrfWSqL2rQnB7up?&2%Ek0-^-0bDzp1+Pk{6g>A$wFM-td zX;fLr4T&MHxe~|hIsDOm5Ni;J;9}Gk2lgR!;9vf%T(%pZbkW!hO`gn4t#yBhp?`UE zStFZ69*}}2Nb;=d+U%KG($%o%O-G%j*}rqxcTwqRfK}{?J_$j0 zl*@h3aFid?<<5gFRT|_SmpLYS6Ra*t6)mEc(g#M6;!FfBsXg=HSYVjS=^(7zVD?V8 z`3HfSgT=J54SIy6iqufk)lSyMMjAdIGr&(gP4Ba$!^Unr9lw`o>jDbDCeiv*2R*h! ze?7ut1HvZjF}Skr18(cueL-OFR(h}UX>neT(^bU%EdS=&aEJ2Qfp8QLNR=r|lLOjr z;c>-*-judnSv*thi_t=PwP()McyIFEIgLYd=gqj|t=}RS1x`ju&OZBGrf{>U3`GN{g>?m_B(X{^eoHBezA+Pt3a&WIS5(??>+Q%P%lD` zA_ehCF~%)`KX3|kYK%c&ZGCZ&es_ceqaJ=yO3X2pU+fpVTNpQ+*KZ%l)VYH~C_R~R zK)n>CtIipjb7OdGm0cV&ka?!QF?wD9PYWde@g>{DtwT%DYD01sje7=4)*_j(w?Z9p zOs%4oCzsS**3P5IhL6jY?yW_P<#?Hge?NXS*f1P%^b=DDAPOYAopedN@@KgrD)Ra8 z9FF|`&P>o#iKp^x@FOMH{xP1*L=H+;TeT56SU3sU27Jr$WtIXB8Bx4egXhJlmwCTq zYu$vDxhJb0<{$+T^o~AbbwCBwDRDV9g+;S^$Y<%6Ekm9K7u4ImjwakRh&5_0W;;ag zH+4CTU!(0q7z3n$u0F9DdZlZ@1mY^*IR)Uzywk;)U4(j`o7H{gpF`vkTK%nOLGPuX?QnnG;QQ#3Q)3m* z6e?W-Bq2FJ^yF?bl7e{R4qE&^EY^g%a!98sX_!=|B)<@gfBW$iA2-#9{;8P};z;V_ z>i|h$82`kH3EcJYgNvDe_~d%&YL1@l{ktHqpPZkhjy!*KO zYG@~>UVW~q&0GCuqc6}A-`zMrDO&Mp+H1n&$lLFjLWCC{#LoHwJ?@uuybHz||ox!}Y#&NfbJG#!JO;*4V#t23@Qi*neND^2KYjREibcB^#-s+Z1qFHsRFpRLylp}~MtoD6Prj!-l3QNd=5$QQvlQp}x z9DR+ntgdTIz(fZhWepS06dpt{VYAO^&xq8NZv~VMbM9tcW5rFG;0Eu!~e|CTi zW>x7XJM8vtd8qBhD}$K}Qm1&^ryX%4;IP~aymvY|Bt9TJcKJp~>H%G+=(f0?e#G&mN@g_K~h-#B^0 zYsLh!>)6zE)S>`Z0DG=8aivr8%`Ioy7EQ+BJ_N!v16)!)54$*Ugny??hz+xdPh=uL zB>!7DCdur6eNZbi(4o8F^ zUcAzIOVR;#zJo2863MioLV?~5j3yd-8VV#f3RV`B214884m&Jo8020+QeW0vh$VWz z;QXa}sKbXS2s)1(1fKwuMXQf!R$B*A(X2N z!gHEtoszM|pJRcusz&*OdaGuM2!Y@4rj>!B~%=w=LsfKEPPdxXO< z^Ux)X6yv6_JLUAfD~6coSDgJ18uwB<*Ln6Nlk@{pSRu8@qXwd|DSO5It)lF{^wl1w$O2?4V6GX)(NhJn+&|U5^?RA zrXxGL;MDlu<}u1akeFq#LmUd|_FDx{eWqtPrFQ&C~NF&&Z8YFqDR|{Iwcq-x3mHsSQDG~5M`3t}XcKWw(W3!Yw?)>PMZTcxp{*dt4 zSso9=vn9*B{)LRvu3L6PSdr`Ls;S)VIFMd8#IWIi19^`l%gU!O4Eg$i%P5sOE zF!kS{$N%gY1)yI2@Cb!p{>wJ_6o_JCIRS@QXb}+K6x;J3M@-ABgD9qY(du6|u@j@Y zFmVc94M?w@CRFkS&UiwQA)#M5pW z2@xZhVcZ`P;#$mW9shRTcDA?ihX|#t?mdr`Ps|I9qFE#6?H2x#^J&Ve;FAZhC{WXw zu>e(r9F4_GLQ=VGboHbe&vBhwj3Cna;y}$A(1)2`+xq-W%Sdtk{ESV-adR1$N**_I zc;HkoYy~ZWWC$%CD{|%j`B5ATq!BILpZiJJJ3u?6Rrhy%-5`6$#jsjfUZkqKI;Ji_Wtpb!yck20Qi^^x#SOLP&X6JL-@vQ;bH5R0n)M zYI9B(441_>ShSRUhDxX5qeb!*)EcSWaAD~~;VZ6x*?>b;2bK(t7PX21&+t$aa>jh? z0q%T&Yk~Xl!k-TZbNehfv7<|UR^lq7V_Mv-O*`$w3^aTX8mZ#PD?MC+(ZYy45qov; z^X;SCft`j=4V1X26N$z4>~*u3X`c_9sbP-r>(M1V|gc*h}4QE_8E$Xz9;zv=dFEZ z0^EJ(+0+Zba(hmcZlYpLbAH8=2)xFaogL|FQ_?CIgiGGW!7s_Zx6Szgc~<-9g*TE# zxwm)@k=R!svz(H#r+{)B;DZt5BJrKX<6A^wM%49cTOB>ftaktUUnVLq(o`>I4>PC}=umj*zg-!1W)!F_7v>9Agz7Y`t41uy+ z!5K|^3EAlv8@}0QqH0n36=U(v?zta(?K@;4}BL8@*RrW;Dp|i|x$OluqthAboe;{D>^=>92&C_$BI)PoBfBdeUy6K(?A2VG{c4d}iFwmXsMg;HACU409dD;$ zwE`%=Ys|+=h42f-J7bC9#gZ>jF`%a(;kvH|kW)h?*D>Oc!7-TI`{>QN>lL7QLp_qf z{9m?1tjl%?K1PHm$=a7zdi`7)Vc|#N#%)@|tSh`c{$$a5cB*8TN?d@XXv=x~z%*O_ z0^8~>_g6kMg&g!50@|H5-1?`*MKATGS()M|I&4`oBALn?W z1d!on0IXK*Ed_2DhDI~=oW|WlRbZ)^QCJ6UKj6R4~pUnY%`Yi^(kY8^l|8e5m8)j zbm25E=c*O#i;=Pzp1P?gM>k4HB1aX>nDqt}r_C0vKR=82*}t+3amsF{;X3e;&n}{g z@iMV^NQg$0_KP&5+WxQTi{H;JQ~qQuZb^M7TX z)K@YW93{>jrM4@+@xtli)0kHdK4G8{ED+vrRZjzNj#I}ex%_a~437Kyc7jUaaWmk6^EBCb-?Pf>1e3%1C#PZ?PrmBM z*^fRy)fX^8)lg+dW*1u9Bqdl`r?=K16@*3&ej9peCR68M$v_>c!K$;_TEdGNdVV!Y z8wdV61!!aPE-N$p;TJb6z-%v|*uiLFa`M+^pDf$NTF3k87pg{IMm(*sq7~&K!8O0f z%70NV*VfuscGSoG$@>;R9df+Hrt3}tOw^bZ{EwUQXJb*19IsM0!5WRXA1W#ete++X zS5+P|B;7$jI>=S$hD+W5&EzW5ZjOpr8=W$6IFfG4e(x_^Hu&s_%F6&za1_(1IIz^F z5fkGprgaH^aq)wtacT1L&f?eemEE(s+zvkfywUywKUBc^w3QXh3@dQ^FC)24^LZ!v>@_b!+=@x$iX_%N6%*(oK$Vby9AQ|?I-?xw$*B{p@Z&ulF<6h z#Xsep4)QT?aJ-*x{BblbycKXTLI5oz(WJPpL|rABHg0vRyLveJE=Nh(KfkBTF&SuCHM@AkKs>j zZ8;k`ONP4&v`rXYtm|BS`%*5+(=<>ZetBTrr&R|NLLe^WtoY)}%~q!R^J`2;7n+HN zbVz9MA3#L`%<1EKGhAJgW3`fdXZ><%FZiiZ(+dXRK|oRSJNYX@i&kY^OEpcO88{QD zitq4pRc*>ZY&hsO-WFb0{PBn)qNjR@?eI0gcBFt-vr0<*Ff$%fE=x4axuZH$4&~Rb zy3}D!+c;{$J-jGhVjv*(fID4ZQ)w6Exv4PndAT3K+A1j~4ETPo9nW2stnTWR6bK9; z`8I&!z8L4({D}sb?l(cUW>N;_4G%z!P9fJ>HZN`@AU8f(k?k6&WXPz@R)M>-`GWY1x>@Y3&CJr>voW3h3X{v|$vwKkpC$|7s9dtC z0O_??UMb&!`ViHZG1yqbiJhhH+~^kp;E3P03!;LI26o z?ap%bp_|qxt#5zlna(EFfjO9WMw|F%)oJRldu2cOlP`bpPKna;dvWEBnQp)j@@@Iy z`&rjYWy{boqC;s>#plwPJjHh_eVgh^)o7UOkgevJi;G*17}KxJ-tE)`Bkf?e#V{5I zRm%)bJf?U7tQddx#Z^kiR?^sP`)S5Q07N|DFe?ANp(?T8+H!{d(;Kx+Th5Y)krLBZ zN+gvyi4!B`+gxQxq2)dQsvxo4CiqRGrwQ*LH{7VcP^%@p1k0hK+R=>LOl}f|7v&sU z$~txAzp(e-QB8IGx^NT~=}M7KR6q~}6r@OtfOP4-D@B?i^csi?(u;JZg2oUn#weQ(ypLd^g?-<|yzI(^H=kD(hMiyBE2y?A9=Wjm0r@WUPTi-d5 zuI`ia&YYg#k~B;#T$k65ku@rEiSgdC)2www=jeL*CBWKKEszNQGz`IiN|fdrKJO`( zbq89XRI2g?xuUhtUBJ1e;rG?2v-=i|J4NVnXX35;;9MZ&zzSIGpiPkJMMr>z z*&ko?dmXm0US#Rq4Ok*E609+mT~<4@c)}&z{>|yqQm4gP;l4GOIkJO#VVdMB{HwZB zrAI}gnv(rynCd{3UP|m;k5W>@f+UN*{duukjqUZEHR4^s3_4No4^U+j;z-0wo;ZeN zY}Ero$#gcF1xo~D?42paxu(Ga>+wl!-@5ieAXY6V07DnSlJo?zU=1c(&j4%0Wv29i zwgH5ry3cyb?N~Od#wVNGy8sRMDUd3*z=qGf>x86P#$Kzs+Da=xILoi?*PY~X}!FTCAybr4a>e=OV+w94`|?npw}VnL%&`& z#ku=cvSO!Vjv?Ve_nIeRCx;%?7IPz7j zD?mF5Pk*Zdfee4;*Mr!xea{ayw3A+H&9`MOXr4LGtRYN8)=fO7Rk^ZSir78(?N73t z9nnzBF5^JcDTDU<35!o@XRKi+{jgT`;1(Vp>BBS}c!rj^u8;S@r0gA_&00!oODs*+ z-S>9jWhplOvTLog@>s@DpvlOvXtiZ|lQ=Q*>QHt#9~fAg_>`*%c%BE8neBMU`Dd#R zcB-TO!B5Y5zQg6Lm!subvzn0ELqs0`rH$Yq@{-etP16Zo_afeo^X8+SiH5_YCmRyH z1|eGogGDoopQLuD{ZDTZcs^Uer7qI;;R@O6I1QNLq->WALE+>}XUT`i#jXUAER^_Y znXga}OHp^_Thz@5aGe0bvgQZc3%?dDA0;^Cb04(7?0+&FtA)Ke+6WEAs-E=62-pi{Zm9LgHj zl=i#h+af}aCE6sFKMxY=zO(M-TMts+K zURdJam-~WoF8Dy|Fn7=G80FpgfdHBTNu`>^I`HRrDqOKaIwn@kq7+CvI2GQeV#@=F zu)fpWkg#WI8_)2KHN8Yar~d?)KH=(EIwGx5SUWuig+ojO+uoRtbEC|b;X^B*xf!vQ zB0Z#2VmFc&wR{_Jdqe=F1A8DMomv&Sn-#P#^~3ttbS<8kIX8naB51uBleVzK(sXd#8X<^dsjb4ACsSY5S37#B;OeJzDQQH8-ocewunDK zbd^l=`5Phd0ehF_q?P$oj-0Rpc#oAD!514-6eaEVKvM6{PtR~Yjj@z_-~Du=U97Y< zRb+bkgE7N6u_*L)FLvdwNz)^8grB{)g-*v-ki0BR-d#qny!cQ;GS1H#)*m=|n$ei0 zsG}P-$~SemMZ^)66LqW<@yH%48c_lAgFZ#PLE%P&kHR&ag_3hh)WSpWT}Z5qTqS=P z0A+vdh1Ch)JSfxsymX<)7CA$klv33&elyLJ z(j%~5uj3?6( z$FS^+rMS+wY;_C(409d2PQV2p!Z3vm!IfdEw;>o#BK@WJimz3dHRL3f;)vUE+W;q{ z^GR{ssTb^g6JV%_IF!@im5;%v$A}+@7$E}ZOdPNH4c6Q}D>E7D>El!0RIH=VR+@ zXwF|DI^Xb*=mmzZdNrgH@uEUF8LalAZ8~_51yrJFA_+ww*L_6HiV*3Czsv#bi&oT0 zNA`7xlJ-8gKS0u4fBDUPzvfW$4ax1{4wuRKj{I-s@Ti-Ro_B$V%9NL(N}5#<5c?9u zq)Um4FrHu->%KZU5%@8+559scZQY3YY-;bw4b(D44})uJfu7^u%c>l*+M z1g`*ttk;6m5$|~jcN&m~lQe^<&NmMo%J={KnXXOT-R|{vD^l5QqoEl>hBQK*d8~eOkO1V1VkO zBmVxFVG{()s|3c=fB{%vuCL|&m6#%g9|68y0N#`BBc54dvnglzwTL$})ro;9Ebzfu z;-&eE8;p}73vL&@BzGgn8!^zGOyiE<6b|Z?KYTP;#YI77KTvY`ki!~!t)J;~- zG2f8;_Zb-ePX5C%3ugmzZu9X6Lss+~2ivAn`SJafbdPoK@OMYbF>|t$bqu zMv?=3m3*`9=32wUpo7PEg$=ZB+g27ZEKgb0@pL;!eX_G~gH9?xxEnbycAH1a2uKmi zNK}NMOw>>(0^4ZhDcI<^wX_U!U_i8w1^mm7c>1sMTIb(zRaQ_{dL;J+wXZG4z#Yp7 z&#;CCXssKT2nT1=q;{`~sib(nxHlqoAf2!5pwd4;RPDUJ$Yq)cNCSbp1n@`QT6mVS z=x{C#_zU*3dw(lyjl~bT14ORBlePRyR4%E>(qiPoeLNx?k5u|8s`bUO{a)=Y$y%^| zq*iUG_)8zj1Y}^7JdKQVVs0b|*z*0C`#RF1_yhD!40g^?2{|fMTP|(-_xUCzw4IT> zg}49I5BRfl7D+b1br&EQpm!DYYgKI;8hLeIfh0QlGTx|c(fF@#{592vAB9owuyF33 zp`6*Dt<5BP7OTbOB>8)BIw$`8a|W&3cC`19Tm`I`g8^}E8&=p}P~;QUBX~@EcM#A9^Ks9hk#kuU$rLX zf%ct%WAf2F-yb0D8fRw>T~3*d=hZfjJUX5Fa>?T0_>0cbrwtcT~ixtUohV(3u zO-Yk}&Xlqmdo>0V+b6qs-R{^>*C4F%UfzsCdcqk_6ld^6rilOdK2-LQHj=kg_-`MQ zzYw$7b)nqD!Hm7v!a4} z{1(a(Tk8LF?s+#Q{nMC6T34BlvU!!>lK|MESPVtf&gZ4Dj2R_ypdY1 zATS@@OU5ym$oz6%6|}N-)pkNjO;t@wn1@U+F(f(dClGS4lrC6TYT}3y=9==zzdlM! zFY-`Z^U?~>XstAKnRWOQzDWD0DB10_gPu+m+>b$KZUt8r(j4}ud7nIZj}cQ_Yzb~2 z^-)?VBJ;;2gLy`^RLjZNkAHDUthF}A&U0AH^GjX58cs^WGun1-9S6r=EzCimtBO)_ zsV)cDHn+hfH7cT`mz3b`n)DaDt?@dNUy86&2rG=n{huXfc(pu&KrG?VKjAQx-Rt9rQ~_I81oulwQuSr7630*2;d$a~31Q24C; zytM)V|I#CE8i2L%CO~n$T$uaj_BmAp;k@S$5S(10XeO)vC-VFiq_a%tDFbz)_2R)M z>BTwy;*J^3=}{E%l)*a^RL+s^NcO|~XU_3;5krT6c`o>XnfvPd|3eja|2vg`|B!m` zf1u0tx9?)Ww9Y)QGABSL;XW0oA+?67!d;X)FQA~ z(x@m6m2&B6l~&G{;*XTkJH2v5@3j$4G zLK;a8S3d%%0etX%A4%++_{bmllS)U%MG69Q;V<7VA|2~2e`&l&z~i0@w#1W9CQZs&97re)8#<|}y(Z{9fo<6XMb#0nGz-{?KA|Vv50?UXekd-Xm5OOLdbJ1Cwin96n7ZTU)QS z@}SX~z4S`w1;)hZ;{skx%RhIQ(v?4x_iSmpvh!GsOF<38%Xw z=yn$aQA=#UWLC^auc<)~c1crZ}P^LgWT1Oef6l&LNFt5UQ zonnsCg?aT=spR)0q=gtDwCAM!a?mX&Q@1eLR(onbI!U~>$-xigObWc}` zED#$NBrzsUfo6iOrt2mU4|g=&L+U%L-!?9LN7L*rt)Tdl@-#K1N-NHMCbn(bI)zJF zd+sF?Edm^BQO6~Na0fgr&w)sWIf?R&q0qskJJGcj422cf)W~mIWiQ2jHp3+wWorCwt*(TD~jP18dVZ%Dl6brf?rDp>7is`$5 zWH;Bf@ZW#=ZO3Gy?`tk1Jn?gcDvmHBk72D<}!?KnJa)h`Mk-1!K*Q0D4zJ!@;2rqqMh5)%J=m%Tqy5oKse{YN|syZ0pA(Nll^yoL(cW%k9TPR zT`j<*?t-E(Xu4)5-@e9)i}~+PO(&;vQkKaLlhc6W(wVOq40?1XtKLiS-#Uk6;iiJt zJXe<`k6xf>O&wXqeK%*FH|ucY>p|3bk1hypyMU#cR!(4|e8MQ&*K@dDji{=BRXL5k z=Ed0rczUX%84^q6yq8*Ga?#v9JuH;G)`}1=zWw_;q}&4ksMHZf5hThb*av)U4+TAOIwP*w(t7JoS`@nqG*K=#ho!c7aYmFG2hnBUT%{}cMMRLP{n z8=p>tm3f(lM{5AbM#omg3Ma-}S&zoxcZy9KK0&>TEm?K$SbyC~a=*@zRksbk0XJYk zb?mA?!^TB>LfC!ZsoF>?Y|>JM)q!Gm4zIE+7x(wCu1A*gu*CboprRH&W6JA6J-Q)w znr_urx9X!T)3#d67i~K9dRXR&;Ha*MVhh>C^-qafhEkLfYVxuz=M@J6H> z-V3wdWjfxDhvGRe6}0P%O?JX@qsy3s&*yQ=8h-vD}im}8uU@|@yRc)FHIIEVkvqgsiM72 z?IXNmB!`66%x0_x=$#W2-y4E#6A&yKLfzmQj>ltOR&?g(PMD}h%j3O#$NUs-ySe-- zE?;m-eWU#fmU}JJ&JGjRJvE~^Rz}(_;NxoH!{+y%lf#fy zZ%A?y7Shg$w;o=pZ^8TlGK;C)%~ypUZTBUw_`dqZ6%#voDRwUUhMWYtjkDWy#tD4$ zF7^(7*ro{o#5Pntkmvb0n^rCKV!$ltlH8N4=@O*zmYmqQ9#xvY7#pjf``OJm78N|8 zUeLS!tipG1C|s6fW>_8ZD3vv<&9E}c8=XBi+6cghkL73~lf{{pW|R%=18;3kyO`po zFr8yY_=wUO{O9@DsO4XRf}$z|kNpk~a(wPzfzM(5$Q@-_eR;aljvFFd?a)ku0tBAi z>T1V&clJZ+Xn3PS)ez__4{$?Iwt-iaV_W&1mt?GgKsT0{vKdq?cYWH~!h)9{)LJE} zTH;DFclC9Np|zt5@X|t6?lzK!V70_0Z2-R!uMxu?lF&NiPU1GNeyVbJ0chAKW9Fo@F9J35v@y0ZXVJA-octYvGndIkfd+ zCOXyH)uJAZ+=~z0+_rpz@gb`91p_9S0?Qw;GZ84Y#`MHdBNiV}EoK4=! zEEgo5o~8Cyyu`(9Y!}483-Mh*?dISYb83$X-C!Q3EW2%$fhmp*NH(IM^TtyZ!Wy|}L?w@BYiOq{>LkryL z2F!r>+*mu>%!PW!Y}qHUAt)^iidE>eU)t~S+o?T;!d z_|3`%Cl2A(01FvAtU>dv<@;ETs?UWTzX=33M1YqbetKm?mJ6;leq0KO5?F1;I^{wi z<-WfXJKZ61Qo+(vJcdik+f4zy!h{Ztq@xwB>@1zBcUc!Tnsc9oNVYgA^!2_2j+JtU;F#uM$N=2aLv$L42>xOX>dyZo~+B?uD`Y?*2ZgR&Tv{C znO%DAidzOaA0cJ`r@r@( z#)bcfEhxhz!KkWXto^prBrXMOI-93l&8vM^&_>>slqA79;ZKBBes%5lbV{R{ItFv< zO>cgGA=sV(`10TIC}981uimePSL$wI96Yz2$FH-BM4uT#{|*BFpWz!`NXqs*kS-yB zyY#wmd6%@i`noB1EtVm5X7^`8z6zcDa_<7QZ+`(k2dmeOWNI}K3Ww_i;%0Gj^iJWN zk=Nf!*~E~N=kY8isB$HmC)3M7gWlA(HUHlT1TX*y&N~3I{L_{B=Q#d{NbKU$_X{>z zlE?SUox(J#8{&T0#fT4x-+mqKIV_{4SvNw^$LvMr&iFd5E7m5AvdedyOQab3yk2_e z?n+JbntX|Wv|RuIj@yC0s3$jssnszh z$g2?mQ~S;g-0IRwfdW?fSv6)qwoXGzGi(Rvnsl?TEuC4BCxz>nr_ILKnW!YdPi#j6 zWS$;RfcXtsEpi4}On-oK6CwhU$c>xZ2N#+gf4aWZ!z*AyQl|68#vh;~T_a`-WH03O zzU0(7(CgXAuMqw>@1cot)auo|3!tU;bDOWuOX&0xz=Ksq9Z7<|{|VkE`8!7X|J21u zc4_1o5}Zh|#apb+`m%f8m*yS({ONA3nL&%~6|yd>Ty3K~LYGe)ya$ktg|y!T);cP8 znkx=fTdFXTxzZ~8?jHbZkbR@sdSgxwYp^l;cs-F z`uo=*?=p92Nb-~U+LY$qL$foaDctiSOxg?_dzm1P@lao`vZon3D`OVfy;zp@>}Wo> z8EyB9TGooR$GoaSHy)uxo{sFjPrewP>wZpm%Ya-jh+voGLar?13>c5VRHlQZIFs+# zj+9Na)=-rJoOT4C&yE$ERYScS@J_ccQ&Eapu@Ey8y@E;Qi`EItnQb8*7a)(3{1us$ zo92^Qx0PN!Z6akN;A6U3DavwO@}guaUDq(mUuw40Wp5XT;K^Cxpv2pwwZg@k=O=@U zC}yFz#uv8eWV*ErT5r=D*G6YwjD782!kPuqT_D?U{LgrUV}BhFy_VmRF7vad z(1N6dKo~;4AgWx@NNqA{&S1Y<%i?%K$}ey|6t0fvS%C!~%Y! z;)U{$)6_(v`ME&yv$U}-&%ELLGK}1H4SgKY9o#Hb;jABIq?7GE|Icn^-_+el*r-kjL+w7v ztnVv!Up1gmt6D zj$2_wuFk#jK9_Sjs|1VS!5K206lmVt$U}Yo`_nGvCtjyG8857JJqiO^%JNdAH@JRm z73?F+(tt_rmaFK=?+?64ZqXo)82`v}rKOa&%ADpu@muoO!TPUs>H5+vcRIvx@)rZW z$;6VncRlNVIW{Ci_lNeF2y@4ke7Q}TtmC*;V%gL-p1rEOPWjhCc~*NlWOPwv?l_JT zPY2wQq43AVJD8ru=(uanR!&)R=a)7Zhvh zy&`Q(OA30cfnQ%@r|2wSZi97MIRq;LMZ@gNs+K1Si+c%8{olGMZL`7>jg`|Fpt>i} z=|#H_gM=2rNcPD3A{A@0*P9!!K)Kd%EWh$BgtBU-;HWgHHW8QLfyvUBNyW~x6-5i zE831GPKhM7i-4yfN4cYyhx2mOr#toWXm3Yr`5z&nznI^;eNZyGy%Ieo1B`MQ@ium{ zNiZ(ganP?sKITEn{#|(kgLn1n)v;tG%!A&Gv_NM#N&~-J>}-9yC+G=PQ`td(O8>4h z^8GYGKQ8HQ@H>&jLBHV$(alyJG?lVZQzVn1P*FexXM=84=_9W9R}!vixjStP)v1(x zpD8_qpsc(~mPB(bLChq`tFLzYzhmlhz|kdc+zL z_lEQ-4d?h2`!ec%5Fa+^)3uyDhl8zKi5BVfz7`_uR~O`Tr$Iv8;p8&TKT(e0(<_8W zfG>j6;vXP623Qx73rMLr=h+e}wCK0vo(hjCtCsh!ipi1xPNS2VSM)meIGRr_i(R51 zV79!S-{o4N&zk6T0JDDjq&s_u* z7%>Pa89OSO3G!b8=1CP&aD%M&GaSat^7Eaam=$c=MI!fB($t5iZ^2|{VAc2!52X5K zO+Su*khx|Ohjz}#ioXMl8pQCTn3)lRYA#;h*;zbk^K2eduO9O1)P)hWnnCxCD1vVY z&(PDYJ1s{f?>jFJAcQ%Ci^K|8r@PXojPO8uqS-J^U>J-=bu2OxB*vE7u(#qbGNF&# zpTls)b^}WpFWKW1rH{~UFqKve**KB1?58j6`#IpE>39p&LdY(~WyH2rD-j8iXp}=7 zqod{BNoh#YW?J|ZbyPvmOSe)=^q#@_53((p3)R)*%6p|D_TeFR;!wc8>N6mOx7YQ= zc-fJwZ4Cs-c*05a@e`x!X; zGIfJST>^grt+rSH)%INf5+?VjYUt0+{{Neg|Dh-@DkKH|Bi{1Br1w*iQzGD z`s7-xB>EWobQGpsO<(_c!|07*`UA9@zx~&0x$*lB5tuLfRVPskae%s#+bs$7uUwly zXLp$xl$IdAQ7E5j0J&2DW$I7OVHVUOU)%T;99uH>@`jnEZ=%w67r0(i{koEq73TO~W;V0X7ILbgSIJ&S+*NGrT(8MULe zbiR*QYL!C2bwl%iq^gW~5Hxk!jUQ$(*N8}E9+nSuUghJvkG9vEQ7@bP1B5$3Wb1cy z<~zzcGD)3nOl^Dol;wlFVtIOc#>%}!tHvHfMb&<7G|POcudW}ylK&zruwP;~auZ-w z8v2%Y63OZ@S$TPQSZ^9t&b8609!c2j)kb!PhudFwGgh*{qFb#a!_j;_-_=Y95F(B* z9lrBX^9{f_YXNzJjWNEi)YNh-WyS?bl>xD@{W1!@H8Zmyomi)Dd(3s9;pVAhtfr~a zzc;wdG!_|ckU)M6;|Lnf62M1ckX;ZOFXP&Yk;gRrJ7Z?E)pM%9du`%=Sa-AW-#qxd z-tPR}A^$jzf0j@$;IIcU4IcjWu_u`{!bPI&-dD6TEVapRaT_qE`pce1fIvMB$LK~R zJN)S+k#k#FZ%VLzM6xoK-XVwcJ*lE1JLy(HqWN!zXos=rriAg$tb27T2WqAq5lf4) zEQ`SeVYtk_^gQ3NXSAR4s;3?hN7$aLauv@>CTKm`avPs~4yLWxX}@FU+uo--`gpd* z&(L`1L3y;HlYuvu>2>t6gp7v!V?CU?7w)@Cy2Yi${vU>)XS31w-Hn*sG zLi^x$2xWHyJ+0=GJq<@+a4!-QvcH@HL5o8Ww9@$ld{waDzIkpD7wCC1tdRsXFB%Rl67MN zmY9vAVeM$wsKnVgfoEjBCl6Z((;sEddaqn$oKJ}=+Os@Hm!%#z)m9l4?%#KQ)vjT7 zAQ|3%*GZf3B)!9$LJvsny2u`*he3YKFoO1G8$bx^!0pXXIoNvzOW> z+0FPq(Y4wygIvOHAM0x)z6g;3_QBGzL9)3urC|mFP*Kz}jPtduu)a${MasgYQF|KX z+X-ZbPGjVOwD@wKY+~+)tmG@iu7LPczT1(q-unjJ2JQPE;|EU}mXmZ$bqY6?lUqKb z`S}jl)Mm_xU}%t>BE8`{@4rqI|Ak&J=|=JvKlsn$@BC-}-G79{cUe8a55^OF{jOi( zhh3Ew-5=dJke6r?TIl)~kDVyyz2c%YE*^uRNr`mPVErZ#l`*J2yS?9gR`z&?@FIEbCxL=1c2wm|tww|`LB%}5A>bYgVE=x*wSTtbmc@#3 zsY9RUN2!dhwm!PR=`t{G{dh_`D+&&875I$T#F%bP>Y6<7au4k=T|sPvIbbs+ROF8JUsu2?~5noMUBI3h?>}O znToftSAOMxTE5u-9TL<6iAEggdR3kPeBcpPbGyGmuMB@$0VfTdwyX{=j}Q|P8=jYv z|HKNtwg@CU0#15Gkq;hQ?FI_TotrTbMunQB9VGfR^NkF!QA>~rS+coJ zt6AaAnUwTWdHb>BK#uL*TRskt65`(Y&b2+=y8f+Fva*z*v25Xt_Swkn1x!XfL_gG(VM(WLB%X z@*?*7W9{4jghkt53ew93^g7yqF<$>$aq~}dd}$+#@$dZSjXeIHLA@?F0PsHtCUos< z!|w@V(i(mbNDannkpkkU%}GRE@!K!h*s>66N5kP+995I8knHlxNVx2Q!x=380RpBw zT3w8OD7nsN&#S+JB9h2rj#t)g!c45Sm0GnI_!WGI?g_%1*tH)=gnk84+JC9ckY+4(m;pyV5NcJ( zL3C>5$#>5=LS6@G)n3lOw3hwgBNyA-S@!LA{33Q!mftam1w)KlPKRCdRp_dcGVC?#(#}7PaIORdNlF(;X|DoDf9guNQpdEEtcH>FNd;*?Z@q-b z?2_3Ju2QknvObs^v^k2rRry{GX;>=+{p80r-eoL9+|2U(27WRfA3?6GzAj&prlnX; zv{87_1AO&%cM#7wtImWVNcUVm-$8ckZY+ax_A%FLf>IlH$ zWdKEgXJ-TAWGkv2jE-%{t-{;_NM0Kt^BzKk70==h7}3> zjwgK*pe1)#zP@hon>1wCY#<93s8OH@wF$wXevXnHr*~^J%-4jupxMO$gm?3egC}2c zN#n*YzD1~Q18kC8xOwP=0svypAH|i`wa957r8pdH>NbLrAl^6yHjT-{>TZ3;)nl(d zpG`*!@vg4!kYskY+vDRgQ$&W|QhOU)<}NF4p2h;Ga@$6dso6Jl++Q5emTPRGYR^4m_Ixt_i*ly+$BDS7X4yQW*_CCYG}rNm z%lkVuE9{&aSXfsj9iCh8E>b&Hp*7(E$CF^3boa~CV6OY8O(YUB{A-Boz6wUz^@sXRM1X+DCWD@;cSs)Ot>e3~}=hAt)kF(RELTvzv?3kTRzC_c&b~F-Zc4Tqx zhkLY9ne76QlMyUsl1ZkoP|M&O+y0(Gc9U=Ow}>t|jKvP$LD!E2hF?$J#1!EIu&`bz z#Ig}HZdjw0rwXIT>3tjdv1V__Dr?bbQD){L2-+Ww`#j~os_nD*`^%(Ya4=G`x{gp; zwN`6XO_0Ax27LE`=yac~`z*tu1daS(<^tvC_y8So0lPbQ+<18#r6Nl}31rMnYb_`e zU#qWwag-t7K;3M0UT%cjqhYk)w{tgqeq6297BaEbz3$TYA?OTrO2Q zS1$7#_A}v>v_#sbz;OdrwI5SAOt~S;X?Ag~uWP0tVjOIMYIhLWv0?h!=&h9;>r2s+ zGsMS0rm3NOlCWhpnWuSw>Vy^ODleL4~ESx~(~>~hN*Ea>mEI-j__QcmaZcHXy1DDKN%i3}ep;i1pN zS}qCDgK<%;!;Sf&H|SU;>)PZL20x;$KEZt077kv5}h!r?yj?TWI?*mt@^ z2BwOzUSu^SiU3|_H|xH+wZMv{XA!wN0evjKGAy5R zH?d?VscnBV%SVy^bHPX6)x@_ZT&p_`l2>xu-&S2j!S%enU_}EfG?#zj-+5A|QS~+v zGgO%=egi}Qb7i45mB@gMKrO#Obl$v(7bkFoozEF@2-eoc3bzKfy`ede3=a$1F2j%{ zHZOl6%RT{V1M-OPB8Gq?l^`+fkz(MTd}8MU|70D-pyX69ktEfi%3`Y7)4w~x+|;Hx znU=1BoywOdvK0adJR8C*kC3|nq3)b3%WAf^gs^SWcs|m8?e|HKqMAsair?gURr+X4BS9b)y7@&lhPpPs;23OiHV<-Rl`Q`2Bs= z>i7c0%3mcOppL6zp4+i_UKEL)G-LvBV3;gCx4rx#xBbTUA~VDp0(O*TKNne?cC0^H zV-2$t;WM3$iFp6S-PlHgBi~3G^#vJcN*ujY?yLW6u7u)qj7Cg0^2b5@53_;(q;`G2 z66P!Rob<#33zWA&;e6}P%*FLH9LJ-m60UUB*}73g$zdkL0^WHE5d}BF-aG#~6Zo#Z zp=j`dQK;(WuGgHoh^9Ojkbe7hvsoTF?+`ys{2fFcnQhfuB+zyDk~*Yo>p0 zGhIS_!f3r5^yzE=1{o6{1Di~4Y}m6Z`aH<1)kNV-8l#0@<^*GrDRS9TGJG(I82(fQf7hzpz zi!&9mD>0oi!N1vud`nA0GkNb1RG$9o%@R<Ilt*iuiqGREvk;)UlN@1m+yyiq3WA zFu4g@v~p`}i-XOVSVrDq*B|NnwA5lLT((OuRx1)4wkrj0Nm#;@Jn2MmV+)~OJP}*0tsP2P!9m7Fh zF-~4)pwhK|h3$r4Wg-;%s;wz$*_)aVYg*Lz4|r(8 z4U!595(xrRRqPB$R}fWJ(a@EuFe5BaF#JjRLf@L%hGc#9-S$Ko(9;mw!1V3NgW;<{ z$C0O-hP^W6vDrS@_C&#sz#o!M|jetNmp>sGi@I_(cTv9jz@W{`LD zh@MDU=T;05iblKw45~@-Fr$g#I5=s!SV~TX=LM&!$b97w`Z%Ehws-z?zoudg^+HP8 zP54Djy|J|U%*=R$v8o((APt^>O8=yIr-ddb~@)a-;W;|kA_)& zsGo2~&r6E(Eyi1RPgJVKwB?t3bZ%4tR*1kl3%&_w7_U8tLd#$RUc9m+HYJW8r;`n; zy=sp@*hVjsV(E8Ot_&G@yudqyEb?w83=&d>^ahzi&s!$z_$_367t~v3OAMKAN4OtI zvGKj#Qmym^S0SP~?8ox3;NVwSeb>|SW#i)l$+XOtdM4cNd~6UP=r`?rZyLWA;u8JY z`o4~L3BSnK;)=en5OUE!K;iAesa>~j&i>jm+~%BT*L?|PukLlb^Pz#$aVeT^*J3IH z1t_TL9D-;j;OYfHMpOYGo8{UI!m^XEzx--s(i^Ve_2lCXP-chZL&|hc;xGv>xWfcwF#k*1LL~QO_h*@ztD3kq3B>Y{}O4x5}=S3_%00 z2si}{P4>d@8gMWgRVT%hx}0gW8%O<<+Lo2k;mOK|6PC&8qjysa&q;kbKIqJ?_<;dM20XX*rk;*r15r8LNS5&OmM4no9%&(B$^L}SRzjvzX+t+3e{7aB- zD++>RKHl`STvWqMzaA)jWds(Uq7ChyosMYkR6e(fAGF_bvgk|I-#wht>PUZ}c%QdU zfTj6p%&LbYP%-e5_>LW#pW=PhIKgd+9c*^c9h1B0+#*EBc}MN~x8a}h`V1J;B7E-YOKw-)qr)Xm?Evx?;<*-d#7jqH>w_%kn+hM zHw!`ZqH^17Fnc-6S~CjEG~G?GATZviV`lih=ON(hZ^>MH>+Kt(yZ4o`>#wLasdwI7 zzt;Y|q+EGgV%u|K?0jnNOFKcGq&opD{V}ZUn+WN{5mA({~2^2PJ^ZClq%;x zw+@s&RGZNIQ8jtg66|5;n#UU1Xr5sUZpq*fF$DPDm#~nofPJbieiLWh0%rvGXl3ma z4qw5j8<+3f>Y>&w9}yH58Fu)#nk-`%#j8(`?pWVfn@pGxYrFLa$Zc)hsKOap6O^}% z46k3Hn1tIbUGNwt4Db5TE;{KdK<$$$t2FqZfj$I(C~evPHrxPrx=8hO1dRx?+M)J`qIzp2Wx2Yn~B04uIrO^#%dMR-qW*(gffo-b|sN$7?ywWY!B|N z=!Jt&cxC#aMSS_Y_U+M7{W;NAy-kq_=&RZOc<$RYLZHk(lFMy0#upz5diMESC;rOe zt=#DC!MkE90K*W2E61hJdRem7+A$_~Tp`Jb25H!_i1N@lGHnvgE_x5TbwZN3`Nhw6 zPqO|}^LARjmA!|ba6~i1g#}(A6+@ro*M6F|Yg{nq>v?~>UjG7}?LTDz%t_OXy&N|& zu~n5ZK)fA#qfxRVGtbG&p=IoFYDnV^RohoIQef(>N9G#$@=7dgZ~S^7>}Nao=(*t9(0ohdl?)?M zZ19p*co4-vzF4_i;Z~Y=C0SOV{>Yw2PkCpw3we+Js*B#(m4_XVw_cX&7?rTz4&{qc zJ(o}wuUu|g_cUt2!*Ci?$Gfb`s8%4C4=yfPNv>sAwC(>hbj#M2{6=KV_|0CAVYS0W zc3;MB=gpo+svK1gBeO|6PvgB4)z%+cy*$9v<`7?OKnvp(-J_L+>TO zT2WI)ZDS6f@bT}cS4PS5S8#y-OwMTq|x(!B12nUruL_@Ys<3VjpBHZ=n@&FfXH|4Nf)KL*xzw)h|JS? zz#=WECZ`MfSW>0ZQAD^bNsmwJ9Y8`NN=q=?OrHJ*OU9 zevSg%9Bi$kiiy0K;Yiv8s7wls$_e~hC33EpylJLIONReP`wfE0PdN9vG{Fc{kprJR z8>$EN71NUo3XRAN)kEGpu1`+Gas8i5LviNcbte?kCjj-u@d>lSt*x%C440J5onk=Wt{K)@4qxE51m0mi3~IJ~OI0`PC#=NTI6Q=ueD~|?s;x)Iw)x^$)O1S^ zvi8mtYMD7zR2IYU0s0B6_(kLt@=TB0bwVWtK#w0?1qPY?GXThJ{Dm{Fw1|nt z)BToqB+HUXX&k1+w-{mq=Ncpx7pHlWr5j)ZVEzwop`?urEq+*JujKoDT+KFzEgh%w z-HVi)$(&bg@AE3M4@aKwDqJt$vOldj z=^ax?!$NVgcc-9}vzFqBSD|j;6qlvh%pQN&fVZ;?O%N)A3Kkslnq@$BJgEQmD6^X1 zP1KL4SAHjDy)k^%hPKe_KCxamXivkvcvz^ktepGyD_HJX-CtTE>Hqt%M@9Ne`{0`4 zP6*&4&=Dk#-H$4-qq0%Kw7Mmacs7Ztd+ugggz>$3k?;sGb)u&EvLWKo9JZ5#bq1=Z zF8<-LW`l{k%2d(bQkgBME2;xLX?d62ul@ceRfSAxH^9TN9>6Miy^cR>|N6Fv!wKVc z&U9<8lDn}=t6p|o9`BTtG$p6x&~Y;`v`@_A3(GE3tVPd7Y&p5v{H!YMdQ3d4D|rqm zdc8y=f5|q3{5Ysa@#VTE-;lQ3w9DC|cUGFGwCP84MYXv-)e48Uh5qs<-tA}G2*Iox zj`R}WVBEFp;7-hd=VZ{`1ZWV&LleRmwo;Y6guI8~*Can*wOc1Dqr%jqbOTtE#tJ_4 zu&+GpWONnQmwl+hUX!*N#73~h#&w!)RuH%=%^ePg9`PyNQpxYr7-_V%mZB9FF>JpF zcgGI*UahfW#J_`DN(q}@Z~d4PL$*}^Z9+f8hf{PoyhO8FFXptI!6l8W;mD#v()FlR zJ1H+OD$!^1tR7d_aQN$Z`0D0~WZ%yH6^F7H343N*bP68Qt|hNjLfkH|Qg=1&T#_kW zOWPr`!fmn0PS(=&xa-?v8~dJ@e?&$+aJR{EST)pD(xes^c7n0NVc5#O&6&Un7ZO+`^oaUW(o)&m~%oig$Za$HyQD5gX zm1jtR9mv3zA%ss>y>VxMfW}Zqp8Ciy<`F>flZRPD;Z01^(IDrd@lwenz6KH)qAzyllNxRX2 z~Li2h+6VT?W8V)8O(@d zru5dG#K~>7=J;cq2d_gHWTM&Bw4NWWm)vcm=^)-!#Mtjzl==qkW;di^StijF*pK;5 zjm)}I3^rp`mCRW2T|2CEF9*1BL{y7;UcGRRC-s4=SqI6tHuBo|-U>Okn_0@{Q&ib- zc`WIBf@mck@qAo%FyFbK#Yv3#9JXF&o5cD_U62CYtZeD{z(CTITb^-JSoQS#fp>iU+hM>?7sx)2~Xu@eq55D_9MAYGc27?9oqQ4x?XU8zcM0R=)0 z1nEV3lNt!UCZU7?A)fW_Z{NMY@64V%=gyuxXU^O^lguQUtd*>1J?mNj@+*>)Y4e9s za1n-@)fQ$Q>q0FkC`~vw!ggBl22+p9S8YK~uK~IyF9NnLmf2UUHArC4Gec`DRS(0h zQDk<+jL9r&aGdEeajR=u=W||d&5Gi`OV;s|9r%DcLGrC~CM&dbvwmJPm1l?ol=3y0 z*z^p>*WK@1xa6_V9!^{7L+GDPJuZEXmG%yh!DWw(^qfL+RP>2D?O8zhYlFD5L6sbh zDGKBWe_0c13u6V^882b$cUN;haPr%1L=Id>3jY^e*42R`r!^d;qV&pi$1f%J7jf!9bRV$_*Ch(J1xW$r z<*+JUTm6$)DPJE9I(_$JF8CB(^Jb=)s|mN;mMNz6`{Q#%7EELJADZ~wv>>GU&6fpw zd5R6f93u9o+sFb)+&bb6Rm53Mfc%4yqCwQlO|j%9P_qR`zNK=G!Jb=vme*&z-=P zH=Ld^8p!gl-JI-99(agy+2}>ij%OB(>XbO8fC&0#FSeJXX0>upA2q~tnA?;Cp=!k|+t>kL_-6DpN#~%PqJZ6ysbC2OBhhd6-$y$?O zm{rAS8SJ3Y;l#kI^h4jus&FsAwov+NnV$bdp;gdtD^4$Qb-l){jv6{By~A5ACzrS# zdr_V24tBi@9R#=ZDDJ74yYY#l&-I8NL#A_km<6AL`~u-8=j)L7J0 z`5khp14)eCbv7akHC z`;fol8MI`!z&@Np)@eMU;A#Hsw3FME=lo{~DIFHJRF+HPptj0&x4wMT=%>UWWsf%v1b4?$lO^84nv!2F&Gi0y>G`|4;8GQcblsFX25#W zKzGivuVYr$!x)n@HH(t`J&H6%8$6b9^V!UOxo@;Pcg!dM6A={&%(LcWd>9-u-wI?k zVfAAzsL_DA89_i=YpRAr|?$pFWyyPS&f0ZZv+KJFrx=(NOFn&C?A$+R0 zGH;R77QwMWH-@(17PzX6O#7P9MFY?3imJJ0F5c@OG_1)? z1MU%J5i1)Zre3p#jvd<#a`*x$;HoM;05xk-1G%#MmTL~N*jEQW-sO>!J{`TQV9~jr zdVnX}gO5wWKp~M(@P}sGaKINo??F)Cce92WXKw&ndq!k+&|GuWjQ z&4+T=we?C}S;we*MYwDF3eQr|q`|{A3NvAI0n@1}y|5ETNU$VkV)AE9yCpDgr4W}c zZLO1g5yHQ1%~wSnd*bj$+uN?I0Fph)vfG-$gU1g&N$C){4W8U6Yo%FwJHuoi2>t1T zf&Kw>F;Z3h?V9A#C!*(`8qJ+c4C#=6EO>cVv6pu*eWe~U*@>IWC=41F?7*_12E_Dh z->b71uU!jqL8D@%McY`<{yx9Pksk=1Y{eBlMk%!F$dR;RsFc~&8N$Yb*hr3%$2ehm z&LGIuYQ*IiUIxSp7e39RUh~cAOtp$SaAny{VKzs`hB^$nhzrGNe~!PA`jqXh%GJ02 z=~B2RwO11Du?8MiUoAPvIg5d`ZQVt_tBZ%N(GEX9Rd)O3uMbbp%$REg?+WQRgG2_U zI&+NsjjibRT_a6b8gQ{pzSw3phQV@_SaQYC!8UZu=V^!_rOw7Jt`KHtYf1-I%mV0f3znBIk$Ztu;y1=)SAR@V^fMFO_jBuLbEKz$T# zzq+BNTQ&4rB6XaTm^B9xW$d|d_Ak}vFLxFtsaz0h2~!J#a_bMxiJ^xYGkItc|28!g zmF=t3KGg191K};V`(bI)-}U(DhhJeU15GQ<1u*MawSVOL4}diVS}*SVcUx-$(7 z-d&r|fd%W8)l>keFHFnS=flkNG%4H)9CQ;}bqkWg&Z+!=%L*Llwk{(r$j8D}B`b?gl zWDXIhJ57y0{-JSpWmp@!EBOH~5@4HspF3;zBQEJ-Cm(%Z9i;HSF~SiPHYZLj*!~t+ zFy&N}QfY2s3UO}d=`k~m0nfRpoF5H6yWb^kyB$i((rTrKi!DHUvfHdJ#qT;uJF6&J8H{CS3 z@}7vhU-^{|N>31xmSU)3xKzGI@%9fw-?WvJRA8nc$te;qp790o+2QgdQrYKMthmLd z7|EUDm1C0k!R}~_NuU;MS(SF+Xu7h`#K?g9HWSTh#465eTixzh&^MLp`o;5wi{%vb z_nAZ;mPc>D_dkc{hzR(U3ld^GAs5MN1MTB;%w)zwrwI;I`}WYnUK~$D-uu4_{F3EA zgj|Y>cZ=fJ)b%yQw8wIokdGHTs;8FQ3&MA9I4zHgzi+ch!`nYUCmK+8Cs3?}n6hm` zH0zu?*Ab2+x8N3^)LA6_JXgg&=bTyG$LievXyd|9yg9-&GKX_pnBRhBZ?nGM|MXPn zIO;SvtuoSyVqjFEW1lm%wgY~Nl7_z;gH6yuW0@@5Sq@*_P&so1>S^+B`cXF`d7}zl zlRUn`A=P`fOMmFyPormDf`el~9+_iAaH(@DT}#<>O3ll`hi=SP*YmzSz0Y-)|GW@4 zK1d*F#EPZqmM?4{bd{WN@=V*0#i-_lvC70=@mamXSnctI%&OjVTRy*>L)Sq3G2c+0 zQRa0IBe$gbjZ)+!7OzzR*Bjm0Mp$D%RKJ<&WkZhV1gy5iJm{@E6&ZZ3uY!dV_8%*%6vsM3DG+_tG)qR z@!GFnQ$9{~I|$Xyu`1jBIB7d5gK>8B^t|bD7bUWtHr>d3IQ3!bB;G<2Hn#1yK%n-p zr|K4n9M+I0e;?bDtbpi?>Es^bH;d#co&O$Y(sB`kI{k*ebfeJL?0(_RU$s+0qAe1b z{lS7ov&|D%IMk5FcZB8*7-#rzi9s*gi^$Pt#e&us`H0pk%uqLp(>GnWiMjfL)BLt` z@f~_=i@WX53=79Xup*)px@m#fgVCiJ_Nqlow%d()B-r$<{#Jw4zA|D;xg}HFYKpeq zd#XAE;a2R-ojz<*o;WP~>)b%RP4iL8t3jIeDDQR$K&Lwd2MR`9!I}?OX^rKh-kSxg z>n3w3zWr1oHqY|;B|7eB$NhCPf5dh;F;aJEaYTUFIE0D;iv`6wf<*iFoiEqubY8p+ofPU604GM)I;6{K z-cv2nX|<|EP=qo;BL`MwQ!7SKO!b4xz(@lkQ-TNGLPoquviJ8LX$?c(hxi9_+Rzb8 z()wXUWHP(Bjc~(=k9~ehbwSL;2)L^Hn}1g2YO^wH?U&cW!>>|xylj`z5!WxadETbI z!=DygRA%EkY1JK$r2;uZOUhJUbwPNAp7NPxuX0V|z_t{QDfAD`IV#gS2GIPmb#<2s zziNc4)-kKQ76)&Pyk1`ywM%i!3>0&I^L*`jv@x`H+X36hdr%x;u5nA>e0*hYGkf!u z#~&L0YqdbEwpC7WxIe-2Nag;DrA1y9MTf0KO01SSsmrceEprAwVDU>n^{_MRV;qQ? zT-||v*2KW`f+)N?xovG|^fIp{j+^kHkhAwp4735D!UC=Y#jS)apsRzRj+q!mKQT11 z5#$3RYN3=hPyNI@o@1i%6UGxIt`LcK$nSAn&jcxvV1`Y65w&-nOUVy;_C2`M%{&0t z2x-GIs|XXYi_z0H&3ro4t7aAkI)xG`ZHM_fMevq~-JnEuvCKDT&hmO#G83;Z$Y3ra z_qQJ~@sZWr5_Hs<;bNscMaW6b`RxxDIjWcaC$yNhlZeTT;+_Y@_U?DKyRE8umAX;3 zYEH31h^g5u|FwdM=Q-T&hxT+NZM;LnWZ7o#k3KU7A{(Exw_75tB+=Wodeb6n!|M+X z6Z{gu>IQKEbo!FEW6hi#r_?9UHaRkRN2a~D;@|t+yI`nzyF4pN?-8$q?0VL@aurD= zT#8&*;nLE0Z&T>pA=EQLvxLdSMm1NQb#|`YLL60^IhF6fx`nZJL&90e#QfhahsL|> z?0CK^QB$pp_9D(P+Q-IyxAR`SzoT`QdtHSVNQN<&P(Mq3&5X0OM&&rC9zFQ(rLt}J z(lGR%g1|l!@SWvH`w%0>JFxA?t0c6X#IRZUq(HFkoii~u8Ur+5eYTc)N<$yUjz*uu zOVh>=w@zGKA7_|)6|3^v;hRckb)bGesyU>7f=kUJQ}o=`>LJhGjK>yc_bPQHD?483 z-4Fb&s?a4wI(zk<(H|NZPoGt*RaHu`#QtY)m@8yx{|NalM*5btmP{$#&|#hBdvv7s zX;G8tn%b4fR(Afo=;|R{*cMSUPGhB${|?)$TPqUuvpe4QO0i;}+9}`Lt!@hB<@3F% z2}eG-EEebr+n=0XUL9F$WNhLNnwXPMc``;31biEC;$%PJ(fx47_T2c}4lAkhpay!r zM7slcdR8V}n&8@IDMQY1n-9FqHtMAuPzTGajmo+%8{MPwYxa|^;vtRQC$X||@ax#9 z)Rix)ArsMM>~nTESH_2@=AQ6h7K)MeQkxGp*J`zjY!WBmFSgiFV;;j)xJqR2-bmOQ z9H_g?!x_hU`Q8%4_+0=zXmHuh+qf~_csp+ky*)FR!E4Cv@znX)J%3uK328Ot=Mo^< z9?QHAl+}VjGjPDen61Bntu0a{9RB3f9qP~*x0BBw*DuCx_Ddxsi|-i*q*14_+)GAA z#C*e0Cd%Vd47u+W*_Pz4c8hQtS_as^$q(x;J#hWLktwk2Vm(0U$~2j39g$_jOcf01 z;dhL-k{dYGPvdz}rR8ICJI6KID*jEAME0TuGw>vML9?KcBA*3_Av&BT&NSx#P<`)o z$P-FRd|2SU!n7y<1W{vo_YX})+1M)p67-Xh_gy-KC@=Dd=9=+UU=^&14Z5P`LH_}* z?UrXYBeyP2sa*|8%b$-IyZQP|7ncHmlBdIgZ-0iN3)Ba(SzU~dr@#FBJHA2G*#4@ zT|QH6VtL%|2aH72Wzae65S1nTT+`!Sdbz;Ml_K&9t|o#i0`*SCgnNa{UOIV{e94bR zVg7}_w!n4G;*#$EdwozH9&RrY{W(1&3yykZS%&9GHuf2J?S=6?ZY2$|nualK(>itz zU;WFqUH2-WXiE++|4nZ0nG@0}fKJ-OI*e@_e$m%|zxHnbdSfN2bY#axjh(_cmi=B^ z220kot(JeB+aY{0%-ZGcV$F~JayD`p@mV{Ft^9SlieX_{iJt>ab;QzCD{Iq>B{3Dp zJc37bTU^2hG?&1%tw>Z}ha8k52$&R11EXnxyI@fv*8CPz_$lXpT$SmRj+ zm;5Wh8K>axm{nCnhS#l$#G)~656wek?}^Hq6x)$MG%vYcV;1bm*>gIss0|-}{#u=z zDtninOn<6^3M_Ml5ij363$*FNFx+r)LT_h$_k!!>qTzR@jsI}`}I@U+wEF0RIMUkED8=xGjKS+AVD>> z(LUQ*vuDDh3d*MmzM-f>esRTEBDZ`_rHo0{kx&5CTFHW1M7osh4Y19RiS4G&B8??2 zkt}vjSZ|S7YmMmrRiuYuyJ*w0!cSB$d()xh&}2pxfmCGV#o}xL+0nO3tyfDR7Zq7d zQ-S^m{p;-7cx9cm+50OYFG4=$;;X*nqdP&Si1w(y6AlXJE9I<{2dA`BOH6Y5QO9q< zqJ%~>V9;a-=+`pYn?_Ym={5Z9zE|zqS_$Xc^!tP`ObVUi+@`Wh;E#yxb@LfKE%NoT z84KI|+QL67cLlA4)8kitpKix@;;)X^|4=A2K5kDqT&8QhK88P3cCm(BQaDj~wOs73);;95VILz6eE@5xEkk>QGW%Q3vOPjfQ-NtQC`uqUCQ>$1Dn zPH|Fi&$crJ(|%)&o2s+A0)I(FM4&Bl$T^1N+ zHK85&*~FXDRi668bz*u++M0cnVyYY(n-dcd=@KTrMmFVki|WHbf4E3#&`{Pc3{!b{ z>T88qdpZgPl1LXxj13~<`&#$y`qYi;VavSQ8|uy~+1j)jIV@MF&osQMbHczd(b8>1 zOw^%Zkr8Op$UJz0`eGsju1NlY!+uDunu`5^Dyj-7%Gq7M8Gijy<2^es``6xGizv?l zW=+Flh?%aLy;n_U1N6rLgShX)`mi^eZ@g^4=)?*01rKJTTt)i2l7z{~;*DgM-&4+e z>v;RsXyjetUoUH)^$(i`W9iv;`}{@1|Ij!>J_bowPHf9!fKsa5)W&^2inKcW#0x99 z^IMUfw>El$)A4#9mOkdS#otR5>U}D-p-Vp2PI^%{icNPDk~5tUDF@$;v*92BvV`GM zLkICrZ|tH_U#Nin?bh@P-N4Qa-r}S>T|{Vpp4T{I0!A2FG>#5pk}HIR%5}35PHo?3 zkKD8b;xxTa4`8`2ukZZiQkf9ZT0gbw+S-T_PAs{id44`vLrlv1c3b7uc!rBd41V<9f%Rvsq>aa{SW79g#NK(=)>+p_#ECGOK&zirekGTj zuh5j1shw9L_dyQkAerisw+f2-=}Uj*e}`0BdF7R3UD3BFda4tPzSNILMnDJuZ@$}^ z|HkwGm%!LE0z7zzzlepFt+xCtIVe{L;O>3?2WFx%d(AZLV|o05ZdLN`a=j2VJs@F_ z$)nww+}rFQ`)=x|*9BNEvTrDh5K0WJ~%_?$F==fo*>_Ys-ppwM~-F*8=WBPZ3Jx+0^km zO`_(|F!K*6uk=Iu;zsh|3rMHU+)WPBE%_;yDcqxn)86=tZ7vwxBmqN7cbjmx_77Aeqsra;4=quKQXhS1O?I7xnXVtm4}eWh1&irTlj{X67G zrT?nJK{+@XEMfd)70R6Y2ydAk_B2og9PbY6|$SMuT5 zv=+GmibF6v_5Y}jyBsMaCdTzwt`vDS18uGWY@=c%Dxrpi7!CI#5iqd0lSe{bwjk_!gLlrIm>vk z-13&BOmE%meBTRFl^x5RMFK2$FW;pf&WV34>OHi6Xhs<-P%%%JES><)Oz0~M0FQn$ z-(N#G{5i)=RNWp6Atm6T-kJSo^l`re9>mA*Xk7e5BYzJv>*Aa5due_LMZDCKDM92* z>WfZJSxD3O@)O^W$$X{G7Nc4?=}|+&ds$0mtX`Hij}8?>HIgRn&#-BT^Jhh4q(ibJ z41d17&^#bAT=96P1E&voHTj&#lhyUfhPW(lG6d&Gyz3iub8h7qImit1rR$7Ow`ASY zt#!*YriKNgcb76rm_Yt%e@=bm#r2*nEm-Qr(BuJdtyahbK&hu>9fAQkLXDC9uB^J@ zG;!|g2%<`@j|5Q$O2yGN-UpMveEWIup17(4JwqP-wX`GgUmMS(Z|><%IyN2JlQXR} z{A1e(-fI(TeqxvomM0J>NTdu2a{``J$*NeeQ3kZlDZNn(L3lmi-3>3N{-{~S64~0K z(GW+`Pd-loPHETZ%_8w^4i4sreLpN(k~`cIqHv8BizKHAY^UXQ!X!~sFD@71oR;++ zQOY;snznnuyL7PrVib8%bk7@0m|4pSEBOdBFrFq3@q;wjegpn|tE7o{aBW-~P(#|< zp?%+_UnwGWmvb905^*b}O~0!r;b!xj%d}^vRh_w=X3f8du#a54 zSr*&DPdkcc;*U6voUS3EKKMsnqD>a#S#^Bov|smYOGbH44o-x-G%8}v86_21g9L6+~zM&IgD>2?Z$_d7>KwsR*ZEKJwb zRF9V)tZ3aFc8ZjW6)a*Da^wssyT9FTjc9RlotlIEG8FOhd^HkTsU#|v!7o#TcNJN= zg;@;$E<$96p#n$)ivzQ$QW&?a&r1yr)TZ1 zV9Z(1AitT8d&c}dlcZuS;mZ_afD3btDsy?teC=i#f_haMaKiLMaMqxhz zj&Wx_S&dbJ;#WpFg^;!nd;tE>KYWjmk*yz?S5B^g;oL4Y@$cjOw^ufAfkQk4w}0i| zeg{A=jt-G4A>K3%Uk~h5f}WqL)G!>AMS>t-SQu@v21dFaIxh+gpPi1ZXoi9BX{kDA zQG~f&DTm<0Rz!%jmKHj;`faDrV5@+T->H71KpQm< z{-=#D%(zI%)gd(iazKlIK41(8RCeL=WTTkaV#m~k#}ZzfxYg?Bye52HOI2)Npb@oDCsAmcbtFAn-tO`J#<%VKKX!z_80sPN;>Z^3C7# z11biI5O-!SjDWMMZCk2KnqtiOuBsto6;<&Fhvu~dV%%K#E^MCdHQ5L zD>3WTIS%f`L^OM(7R5;8nP{xmBwdAPXkDWxkC!Fjte7lJtr|~Hm!sR3pQlkA7 zO6lCo-%1{#o5Km-#@y!pT{Rjuz=)Ut9eRJr2`+)kvdO?N4BVF-$~T{A&aw^EL~Sdp zOC7(X%Cs&%Qa0;8ilA9rD{~}|ON^Hx=4~mrq8F{F2EZoZPke_hNnMC}&2*L5v)lBi zls`0^Pw5RcJT5I-Q;h7GoPgal++qe4!1!cf11s{A za^-gV(_QT1J-#HiPGA%qe1HwCDY4{HlI0ygi5#8JixD?oa^G)Q;?HG>rw6V$8RLAB z=7TuH2^`0}$KSia(?WKE44%eJ6OIV^`F?zn4LF5pwE}Rofk;H)U&z(}-_Yv#f1=NE zZdTb8Gi+NQ)3ak{SRdPy)&pypQ2eQuG)FN5u9Sdg1nE3v3DxOqr8{(>(``HMm5_a} zsO|=Pxf-eK=uM+S_W;Tt6yu`8ffHwX6w1UNrxs^TavV8Pa@`L#3Lg`LZ@AI@VTDTB=*9 zVkxzIm@4U8qcMWqdW~|uF+Tg37HIMttXSFZn+UNiveSyA+zWGIjl^DGlOf(-asvsP zO!86Fqz6u8*Hr>sdG#{Wv-|4;eu ze->))L0afg@0o&!T~9B+y<7NChKoS?>&tV*#5$ntV^dLLOtshlLt_P8$r=$o|G6t5 zJ7m(RI{gN);K-2=Caj5<7>(7xQ+r2txX0;sVc#|T^3L174|;X3aT7|iAzN4zQ*fi+ zN|Xe(SSMGxqQdjtOfqhZfO4?BG(%j!bQC|dOKcn<~2e@Ru3p ze^dLSa%G);`NEl$S4VcXD0$Jvv+0ruDe^g@ZHuW^n2J5&q!ar(vV~vdmIx^%B00Xt z^L1(3J)sy@0@F>p^CJAI>0s|-ZAJIq;-Q-S`rPeGRGFVd`Y1Gj6UDy1@8jfQ*|UCJ zQsDY*TE&6@HX)|`%;MSLS0L*zV8cvF!LO#0Q;K};Uxq4&Xow5l)nWy@UqO2y9Ort! z$QD52z1Toyr%O8h@^@c}A_AWDI4$2s*i(O!b!*=$ zT&W<+t8)=jg&Gq25g}OXA+38DDt&mzBSzG95`+VVQ90o+!_0_s-%iu=kRiWrAZ};} z0m11sccpt^$$6s>ZnvsZ8cON|L5B8$2}fR{W&G|fx*9cQ5q;ikJKGcZwO4RpISmm3 z5}NG+$~6Qb3(=y!BAImBNRq68&F<~&Ho+GaI)hM~*mL_?oEuGNh6X+$9AYn#BPKaG zOtIl#V;nHA)6u%pomJjPK4&71s%` zXi)T8lRadLenBr}9y@hu;bRqHyD6CdW6)2JB45OE^4x{g`P)CV@-@?4dbKS6)1jS%VFUQ>yRmKyEWO=;>+Rad!rAYv+KoIE9a-vOCYw z#Rp_4w{m-xB5doUhhH$<4SkXE**eYnt%(l9q3n!rT89-4sys@Y_-eB~wefVcV%idP zQ7f;PN6Ygat8)K6gUBi=L!tP~mmFZOoWmK$P8u4|15+9T#b=#XkJHV^)<^X86I?Q? zVc3Dn0~K3R)!;s!kvcecXT&o@4Pg6zO+Lexmng=dAMDC}P5&OPP(t&Dt`A_|z0^)+ z12n%~fE4aQZ5d<{sK#QVKvjUuG$s=K1AVRNQIsrxYjX8B%NWgR2fKQTZX=y^X3|Ec za06Kc2YMI8Mh3#6OKDrcPK+xp5l)B}asRoXy=&9U#2S7Fi_QW)D7TwsOGW)55@xLJ zpJ6C8F*Xv3Hg(mc{z`E^_m3Zd6<*L*s@r2UPdUYnC%AEOpJQm&L|{q}QzIRBTIpvpOvK0ZNW*;RYQ{ zQ}XhxZ_skaj+KPX9$AB7DeBXM)uKYua8YoCfwAtaV+NAP@f7sMAm^g*grde|-m3)v z**Vg*S5xRja2l1pb4wo=eU^G+M34aN&We|oO#c?u5Av$nE`5DlQEd+`{|W{$#qIOvlLls9m_wS30JJvaNW z&bXSlK}SGX1Q-zaao|MS#1!D-idikD>|b2-oM6jn=b>!-G~41N<tA6J zWfOKAv<|Yoz{|Y_w_c15#`3C&%*zu&v0kq9l41L!6?KHhvXtAKg4f^Gb3C=xd~NID z5dHOC1P<(k|CmWfPAP#a2e%mS{rt9`Dx>7tvN5MF9#OIgeNX?n=W~C{eZluobDRA2 zT21XHy9Bg%6>50g%n|P@QMC8a)DUxrEuXJ| zxF!Jbk<8!Z_R2>VJdr^6>a4LlX0Z={pNTrJAi%HnRBW6=??-_A1YdxA%x96!vB2nj zNPnO<3oKu#EySzc{-9OmJ?id6EJczS78-PxI%KMeDu5dJ*0daZPU{shMoyuJde3NC zU&?6y=FKURf%eLlVD2zTpITnl_b))^Ve-Zs*z1$e%$xgy?2U7WvielhCe2ExJWy%Hj}=^Fg;_zeOj zNGPmj_whJCj|uzvatTZAvr?5TY_|L+T`Ha%bWHk$fp)9P)t`T8BKsD3T)CYsuV<20 zJedQ@ocvpgHFOaZJjX>w1lksK2j)O6IduXM*p3Uk_K%j`7s5O`qzC6G zI@H+6(KyMo>q^uw*&8={IxSHr{CVd?<@XkrV7<4*4%3nZ_5jtGZKUkW}7wYti$K*`M_E#3^cb2!zYg1!06xQ>qoUnmY6_e8=9yJ$q zX5s-nhIV@itRlfRFwk2G!b;I3azx7@nAT1Eb4b06bib#Zz8^^y#4%6rE`KNSc@&l=Y}vjkgWPGmX> zp%7MQ453yS02iDvpKwgJEi+7q`uNc_xg8?H%67(j`))5*-xXW!tUP;JgFCC*O$NFybatQ2sGukGF%#>@lj&0Yb?8*=aw&mw)f9Wm-2?XJz2|X$D*> z51MKfzGE}&Il$uEF|N@6C1+#jEX%F_!>LD?wJ>^$Tzwu{oU$;}hm|8ten!~dZy#$Uh3RkJqNH-3|1zE7ryVd#0STBB^@6P3*Cs!s1{I}J_ z;Ex7(nz+`RE`KL79!BrXqfgAB{vi#m%M!z9AMvJis&*gm(`IG|&8^E9ukG(aT&2qr zB3o*!bLO}8A)k3~>4zv>ygPedh1sx4hVp5D4Y3|wR_FSoC zMn#?z-%W^GjJUp1{D4vQ1~v(Z%=hl!93goW{X*r(8ynZ_L-dZp8>?U8aB|YZ04+tY z;XU{qp!7awe?_}+7V35(#Uc5dZ3yJN_g({i!r;7dI>nR#3PsBkb2L#;)|=SI+Vi#| zF8npeWyV4Sxp${?T5W8Q@~Dn%LX_{Q@GZl4))-0Y#raJ=b{vsuyM9iz;f*MGX4-+kQbBg z^RM7Goy1T%w&P>)L0CV-aqsqL@Q$C)Re zgG7*i^vwtwr#UrZ?$GA#MU9c$%cHJ>QJ;o8KJ%R6jY{U~RG{ZnnaGAK6Gh&sUEXjl z{ted~lRc5q5@{t(JtwWZw?E?f73RRz{{|2!9|&TlJgXvzwCQZy>_P6X)#rLlTbkUD zD!p61b>k7e6D^?&SDaeJsdvY8T5|_Afw7X&aMDkD>`^>yKGiNWY--u(x$B>TzE4bh zFa}T)<**Bw(9-=n8(*Y-D7Lrh!{YfBx5JMLLbjP-@Mc+OJQ0^>fxc>Z5jpFTuEDgP znYOXb&2)JGW1$cBv{$|+3E;j1K7!y6B=6%8QpzGy{pk&PsIIVQ5sUsc&~_ z&HYNxtr#gzIga3qYJ!dvhnbprB^s}*pC0MWIcd6M?-+1!fqWzCG?ZALCqVQLXMHG7 z)_Zi=bSDzcUB!{GESHP~Sy{=Cmgk8RZ_!%NLNZi?kdJ~ccV}zon$)9bGKRV~&fC6K zdnLd(*`eV??Nbx_J^?q(Cd)q~goVoTD4uIoJ)QepF(=2oMAOyw{tQjvT)J`aL@ni9 zErI@>*DObSDkg?HP}mY5UWM2S2>oqhL9>@4Ku~|9T4Gw2qn9|`)F5tIJuT=q>Zta^vUU;FMfaI2SZFn2`6A%8^_=g`os(l9+f1}|zws_+ofIvESrK}??F zn#V5|QV&o(hK_6(qlBbllP>TkcRuTq#bH^h5SM;$XnQSS*n!&36W1H@+6gCeW{}F| zcls${Id^~mfQg!*L4Gy@dk#@a2hU7ifn8rS`?>1$)ah=Ypn!gKMeHYb{v5zFVkx(9 z&vFW@709w)H?1cjd1W0HcpJ~-jI~J48tWIqrTar#tirwh`$HB^Mf8WgG9;AOJo{`$ zXUy7H4q905XwJm=&S-dJxSPZgEy_&qV=31%na9+oznWRt8VsWs=(eaklIgH0Z_6

J+d0ec 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 literal 0 HcmV?d00001 diff --git a/docs/content/patterns/avd/media/avdAlertRulesCopy1.jpg b/docs/content/patterns/avd/media/avdAlertRulesCopy1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..034153a2e58347b07652bc056d12b4f5011bd004 GIT binary patch literal 28183 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